短变量声明和“变量已声明且未使用”错误

时间:2018-09-11 18:04:41

标签: go

我偶然发现了一个奇怪的问题,下面的代码无法编译:

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

  

与常规变量声明不同,简短的变量声明可能   重新声明变量,前提是它们最初在以下位置声明   相同的块(如果该块是功能,则参数列出   正文),并且具有至少一个非空白变量   是新的。因此,重新声明只能出现在   多变量简短声明。重新声明不会引入   新变量;只是为原始值分配了一个新值。

2 个答案:

答案 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中也有说明。