我偶然发现了一个奇怪的问题,下面的代码无法编译:
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))
}
出现错误(代码是linter推荐的代码)。
$ go run main.go
# command-line-arguments
./main.go:13:5: tm declared and not used
请注意,确实使用了tm
变量。
但是,如果我添加了else块-一切都会按预期编译:
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))
}
这看起来像是编译器中的错误,还是一个已知问题?任何想法? (我正在使用go 1.11)
edit:到目前为止的所有响应对象。按照:https://golang.org/ref/spec#Short_variable_declarations
与常规变量声明不同,简短的变量声明可能 重新声明变量,前提是它们最初在以下位置声明 相同的块(如果该块是功能,则参数列出 正文),并且具有至少一个非空白变量 是新的。因此,重新声明只能出现在 多变量简短声明。重新声明不会引入 新变量;只是为原始值分配了一个新值。
答案 0 :(得分:9)
此部分:
if tm, err := time.Parse(...)
创建一个 new 变量tm
,该变量仅在if
语句中具有作用域-而不是您声明为var tm time.Time
的变量。
if
中未使用此新变量,因此会出现错误。请注意,您也没有分配外部级别tm
,因此fmt.Println
将显示零时间,而不是时间。返回解析。
要解决此问题:
声明err
并将您的if
更改为:
var err error
if tm, err = time.Parse(...)
注意,这在GO中是一件微妙的事情,并且是错误的相当普遍的来源。实际上,:=
语句可以与已经声明的变量和一个或多个 new 变量混合使用-如果已经声明的变量在相同的词法范围内。然后,:=
仅自动声明新的变量,其余的仅被分配(与=
一样)。但是,如果在新作用域中使用:=
,则将在该作用域中声明ALL变量,并屏蔽具有相同名称的所有外部作用域变量(例如在if
中;请注意{{ 1}}条件不在括号内,但仍被认为是位于if
块内; {code}
和GO中的其他复合语句也是如此。
答案 1 :(得分:3)
您的if
语句声明一个新变量tm
,该变量仅存在于if
块的范围内,并且实际上从未使用过:
if tm, err := time.Parse(time.RFC3339, "2018-09-11T17:50:54.247Z"); err != nil {
panic(err)
}
在Go中,:=
声明一个 new 变量并将其初始化。您可能是说:
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))
}
Tour of Go演示了:=
快捷键运算符,并在the Go spec中进行了解释,后者包括:
这是带有初始化表达式但没有类型的常规变量声明的简写:
"var" IdentifierList = ExpressionList .
作用域在the Go spec中也有说明。