【问题标题】:Short variable declaration and "variable declared and not used" error短变量声明和“变量声明但未使用”错误
【发布时间】:2018-09-11 18:04:40
【问题描述】:

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

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)

编辑:到目前为止所有响应者。根据:https://golang.org/ref/spec#Short_variable_declarations

与常规变量声明不同,短变量声明可以 重新声明变量,前提是它们最初是在 相同的块(如果块是函数,则为参数列表 body) 具有相同的类型,并且至少有一个非空白变量 是新的。因此,重新声明只能出现在 多变量短声明。重新声明并未引入 新变量;它只是为原始值分配一个新值。

【问题讨论】:

  • 谁投了反对票——你能详细说明为什么吗?启发我
  • WRT 来自 GO 文档的引用:这里的关键关键字是“相同的块”。 if 打开一个新的嵌套块,因此这不适用(请参阅我的答案以获得更详细的解释)。

标签: go


【解决方案1】:

这部分:

if tm, err := time.Parse(...)

创建一个变量tm,其范围仅在if 语句内——它不是您声明为var tm time.Time 的变量。

if 中未使用此新变量,因此您会收到错误消息。请注意,您也没有分配到外部级别 tm,因此 fmt.Println 将打印零时间,而不是 time.Parse 返回的时间。

要解决这个问题: 声明 err 并将您的 if 更改为:

var err error
if tm, err = time.Parse(...)

注意,这在 GO 中是一件微妙的事情,也是相当常见的错误来源。 := 语句实际上可以与已经声明的变量和一个或多个 new 变量混合使用 - 如果已经声明的变量在 相同的词法范围内时间>。然后,只有新的由:= 自动声明,其余的只是分配(与= 一样)。但是,如果您在新范围内使用:=,则所有变量都在该范围内声明并屏蔽任何具有相同名称的外部范围变量(例如在if 中;注意if 条件不是在大括号内,但仍被视为在 {code} 块内;for 和 GO 中的其他复合语句也是如此。

【讨论】:

    【解决方案2】:

    您的 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 中也解释了范围。

    【讨论】:

    • 请阅读 go,文档 - := 运算符分配现有变量或声明新变量。条件是至少有一个变量必须是新的。
    • 我已经阅读了它,并且我在 Go 方面拥有丰富的经验,这就是我确定您的问题的方式。这是修复。在反对它之前考虑尝试一下。
    • 为清晰起见添加了更多文档参考。
    • 我并不是说它不起作用。我在争辩说我正在做的事情是无效的。在您引用的 Go 规范中,请阅读:golang.org/ref/spec#Short_variable_declarations
    • @Alex,:= 确实可以分配现有变量并声明新变量 - 但文档清楚地表明,这只有在声明处于相同范围内时才有效。在您的情况下,if 打开了一个新范围,因此不考虑任何现有声明(例如您的 var tm),并且 := 左侧的所有内容都是新声明的。
    猜你喜欢
    • 1970-01-01
    • 2019-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-15
    • 2011-02-26
    • 1970-01-01
    相关资源
    最近更新 更多