Golang混合分配和声明

Pro*_*ade 16 variable-assignment go variable-declaration

我开始工作几周了,而且(再一次)我偶然发现了一些对我来说很奇怪的事情:

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

// Works
a := 1
a, b := 2, 3
Run Code Online (Sandbox Code Playgroud)

操场

我想同时分配两个变量.一个已经宣布,在一个优越的范围,另一个不是.

它不起作用:编译器尝试重新声明前一个变量.但是,如果在同一范围内声明此变量,则它可以正常工作.

这是为什么 ?

小智 14

您所经历的通常被称为"可变阴影".当您使用:=内部作用域中的任何变量时,包括在语句中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)
Run Code Online (Sandbox Code Playgroud)

输出:

Example
8 bytes written
n = "Example"
Run Code Online (Sandbox Code Playgroud)

有几种不同的方法可以解决这个问题:

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

相反的效果也可能发生,您在内部范围内声明某些内容并且没有意识到:

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)
}
Run Code Online (Sandbox Code Playgroud)

还有一些不同的方法可以解决这个问题:

  • 在使用之前声明所需的变量并使用正常赋值 =
  • 将第一个:=if语句分开,因此变量被声明为预期的; 这允许您=在该范围的上下文中使用该变量的所有其他实例以及它所包含的任何范围
  • 更改=to的所有实例:=以修复错误

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

在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`
Run Code Online (Sandbox Code Playgroud)

当然,如果你没有声明b,那么你会从编译器收到一个错误,即:=第二个声明的左侧没有新的变量,这是一个迂回的方式,说你试图声明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`
Run Code Online (Sandbox Code Playgroud)


jnm*_*oal 7

根据 golang的文档:

块中声明的标识符可以在内部块中重新声明.

这正是您的示例所显示的内容,a在括号内重新声明,因为':=',并且从未使用过.

解决方案是声明两个变量然后使用它:

var a, b int
{
    b, a = 2, 3
    fmt.Println(b)
}
fmt.Println(a)
Run Code Online (Sandbox Code Playgroud)