I've stumbled across a strange issue where the code below fails to compile:
func main() {
var val reflect.Value
var tm time.Time
if tm, err := time.Parse(time.RFC3339, "2018-09-11T17:50:54.247Z"); err != nil {
panic(err)
}
val = reflect.ValueOf(tm)
fmt.Println(val, tm, reflect.TypeOf(tm))
}
with the error (the code is what linter recommends).:
$ go run main.go
# command-line-arguments
./main.go:13:5: tm declared and not used
Note the tm
variable is indeed used.
If however I add an else block - everything compiles as expected:
func main() {
var val reflect.Value
var tm time.Time
if tm, err := time.Parse(time.RFC3339, "2018-09-11T17:50:54.247Z"); err != nil {
panic(err)
} else {
val = reflect.ValueOf(tm)
}
fmt.Println(val, tm, reflect.TypeOf(tm))
}
This looks like a bug in the compiler or perhaps a known issue? Any idea? (I'm using go 1.11)
edit: to all respondends so far. As per: https://golang.org/ref/spec#Short_variable_declarations
Unlike regular variable declarations, a short variable declaration may redeclare variables provided they were originally declared earlier in the same block (or the parameter lists if the block is the function body) with the same type, and at least one of the non-blank variables is new. As a consequence, redeclaration can only appear in a multi-variable short declaration. Redeclaration does not introduce a new variable; it just assigns a new value to the original.
This part:
if tm, err := time.Parse(...)
creates a new variable tm
that has scope only within the if
statement - it is NOT the one you declared as var tm time.Time
.
This new variable is not used within the if
, therefore you get the error. Note you also don't get the outer-level tm
assigned, so fmt.Println
will print the zero time, not what time.Parse returned.
To fix this: declare err
and change your if
to read:
var err error
if tm, err = time.Parse(...)
NOTE this is a subtle thing in GO and a fairly common source of mistakes. The :=
statement can in fact be used with a mix of variables that are already declared and one or more new variables - if the already-declared ones are in the same lexical scope. Then, only the new ones are auto-declared by :=
and the rest are just assigned (as with =
). However, if you use :=
in a new scope, then ALL variables are declared in that scope and mask any outer-scope variables with the same name (such as in an if
; note that the if
condition is not inside the braces, but is still considered as if it were within the {code}
block; same happens with the for
and other compound statements in GO).
Your if
statement declares a new variable tm
that exists only within the scope of the if
block and is indeed never used:
if tm, err := time.Parse(time.RFC3339, "2018-09-11T17:50:54.247Z"); err != nil {
panic(err)
}
In Go, :=
declares a new variable and initializes it. You probably meant:
func main() {
var val reflect.Value
var tm time.Time
var err error
// Note the change to normal assignment here instead of :=
if tm, err = time.Parse(time.RFC3339, "2018-09-11T17:50:54.247Z"); err != nil {
panic(err)
}
val = reflect.ValueOf(tm)
fmt.Println(val, tm, reflect.TypeOf(tm))
}
The :=
shortcut operator is demonstrated in the Tour of Go and explained in the Go spec, the latter of which includes:
It is shorthand for a regular variable declaration with initializer expressions but no types:
"var" IdentifierList = ExpressionList .
Scoping is explained in the Go spec as well.