小编典典

Golang混合分配和声明

go

我开始与go一起工作了几个星期,(再一次)我偶然发现了一些对我来说似乎很奇怪的东西:

// Not working
a := 1
{
    a, b := 2, 3
}

// Works
a := 1
a, b := 2, 3

play

我想同时分配两个变量。一个已经被声明,在更好的范围内,另一个没有被声明。

它不起作用:编译器尝试重新声明前一个变量。但是,如果在同一范围内声明此变量,则效果很好。

这是为什么?


阅读 415

收藏
2020-07-02

共1个答案

小编典典

您所经历的通常称为 “可变阴影”
。当您使用:=在内部范围内的任何变量,包括像语句iffor尽管缺乏支撑的,一个新的类型和值与该变量关联:

n := "Example"
//Prints the string variable `n` to standard output and
// returns the number of bytes written in int variable `n` and
// an error indicator in error variable `err`.
if n, err := fmt.Println(n); err != nil {
    panic(err)
} else {
    fmt.Println(n, "bytes written")
}

//Prints the string variable `n` to standard output.
fmt.Printf("n = %q\n", n)

输出:

Example
8 bytes written
n = "Example"

有几种解决此问题的方法:

  • 在使用变量之前声明您需要的变量,并使用常规赋值 =
  • 使用不同的变量名
  • 创建一个新的作用域并保存变量的值以供以后访问,根据需要使用变量名:=,并在作用域结束之前恢复该值;通常,仅使用不同的变量名会更容易,因为无论如何您都在创建另一个变量

当您在内部范围中声明某些内容却没有意识到时,也会发生相反的效果:

if _, err := fmt.Println(n); err != nil {
    panic(err)
} else {
    fmt.Println(n, "bytes written")
}

//undefined: err
if _, err = fmt.Println(n); err != nil {
    //undefined: err
    panic(err)
}

同样,有几种不同的方法可以解决此问题:

  • 在使用变量之前声明您需要的变量,并使用常规赋值 =
  • 将first :=ifstatement 分开,以便按预期声明变量;这使您可以=在该范围以及包含该范围的任何范围的上下文中使用该变量的所有其他实例
  • 改变的所有实例=,以:=修正错误

请注意,当函数返回多个值时,在后两种情况中的任何一种情况下,您都可能会遇到变量阴影问题,但是可以如上所述解决。

在Go Playground上尝试两个示例。

最后一个示例说明了声明和初始化新变量,b同时还为现有变量分配值的组合a。没有创建新的作用域,因此您不会隐藏原始变量a,可以通过a在每次赋值之后(但在下一个声明/赋值之前)打印的地址来进行验证

a := 1
fmt.Println(&a)
a, b := 2, 3
fmt.Println(&a)
a = b          // avoids a "declared but not used" error for `b`

当然,如果您没有声明b,那么您将收到编译器的错误消息,即:=第二个声明的左侧没有新变量,这是您尝试声明的一种a回方式。a在同一范围内两次。

请注意,如果仔细应用此想法,也可用于查找阴影变量。例如,您的示例中的“不起作用”代码将为打印不同的地址a,具体取决于a内部作用域是否已声明:

a := 1
{
    fmt.Println(&a)    // original `a`
    a, b := 2, 3
    fmt.Println(&a)    // new `a`
    a = b              // avoids a "declared but not used" error for `b`
}
fmt.Println(&a)        // original `a`
2020-07-02