我开始与go一起工作了几个星期,(再一次)我偶然发现了一些对我来说似乎很奇怪的东西:
// Not working a := 1 { a, b := 2, 3 } // Works a := 1 a, b := 2, 3
play
我想同时分配两个变量。一个已经被声明,在更好的范围内,另一个没有被声明。
它不起作用:编译器尝试重新声明前一个变量。但是,如果在同一范围内声明此变量,则效果很好。
这是为什么?
您所经历的通常称为 “可变阴影” 。当您使用:=在内部范围内的任何变量,包括像语句if和for尽管缺乏支撑的,一个新的类型和值与该变量关联:
:=
if
for
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) }
同样,有几种不同的方法可以解决此问题:
请注意,当函数返回多个值时,在后两种情况中的任何一种情况下,您都可能会遇到变量阴影问题,但是可以如上所述解决。
在Go Playground上尝试两个示例。
最后一个示例说明了声明和初始化新变量,b同时还为现有变量分配值的组合a。没有创建新的作用域,因此您不会隐藏原始变量a,可以通过a在每次赋值之后(但在下一个声明/赋值之前)打印的地址来进行验证:
b
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`