为什么即使左侧没有定义新变量,else if 语句中的简短变量声明也不会编译失败?

Why a short variable declaration in an else if statement doesn't fail to compile even though no new variable is being defined on the left side?

以下代码预计会因编译错误而失败:

package main

import (
    "fmt"
)

func main() {
    x := 10
    x := x + 1
    fmt.Println(x)
}

编译错误为:

./prog.go:9:4: no new variables on left side of :=

所以我原以为这段代码也会因错误而失败:

package main

import (
    "fmt"
)

func main() {
    if x := 10; x < 10 {
        fmt.Println("if block: x:", x)
    } else if x := x + 1; x < 20 {
        fmt.Println("else if block: x: ", x)
    }
}

这是输出:

else if block: x:  11

为什么即使 else if x := x + 1 中的 := 运算符没有定义任何新变量,第二个程序也会成功?

在这个例子中,

func main() {
    if x := 10; x < 10 {
        fmt.Println("if block: x:", x)
    } else if x := x + 1; x < 20 {
        fmt.Println("else if block: x: ", x)
    }
}

你在两个地方定义 x 变量是正确的,因为该变量的范围在 if 和 else 之下。像这样考虑这段代码

如果你在这里看到我们有两个块,块 1 和块 2

两个块都有自己的作用域,您在该块内定义的任何内容都无法被外部访问。

试试这个,你会得到一个错误。

func main() {
    if x := 10; x < 10 {
        fmt.Println("if block: x:", x)
    } else if x := x + 1; x < 20 {
        fmt.Println("else if block: x: ", x)
    }
    fmt.Println("What is the value of x: ", x)
}

错误:./prog.go:13:45:未定义:x

因为您正试图在外部访问该变量。

根据 Go 规范,这里是 if 语句的定义方式:

IfStmt = "if" [ SimpleStmt ";" ] Expression Block [ "else" ( IfStmt | Block ) ] .

稍后,在 Declarations and Scope 部分中说:

An identifier declared in a block may be redeclared in an inner block. While the identifier of the inner declaration is in scope, it denotes the entity declared by the inner declaration.

现在,if 语句是 implicit block:

Each "if", "for", and "switch" statement is considered to be in its own implicit block.

那么从IfStmt的定义可以看出,在关键字else之后可能会出现:

  • 一个Block,即else { /* code */ }
  • 再次 IfStmt,如您的情况,即 else if /* statement */ { /* code */ }。这意味着 IfStmt 是递归的,它是一个隐式块 within 另一个 IfStmt (仍然是一个隐式块)。因此满足重新声明标识符的条件。

也与显式块进行比较:

func foo() {
    x := 10
    {
        x := 20
        fmt.Println("Inner x:", x) // 20
    }
    fmt.Println("Outer x:", x) // 10
}

由于您的第二个 x := 在另一个范围内,您正在处理两个不同的 x 变量。所以它对你来说看起来像相同的 x,但它不是,即使它基于外部 x 的值,它也不会影响它。你打印的x是里面的x.

考虑这个 example:

package main

import (
    "fmt"
)

func main() {

    // Define x in the func scope
    x := 10
    // Print out global scope x
    fmt.Printf("x1:%v\n", x)

    // Not allowed (x already defined in this scope)
    //x := x + 1

    // Allowed (different x)
    {
        x := x + 1
        // Print new local scope x (this is a second x)
        fmt.Printf("x2:%v\n", x)

    }

    // Allowed (different x defined)
    if x := x + 1; x > 10 {
        // Print new local scope x (this is a third x)
        fmt.Printf("x3:%v\n", x)
    }

    // Print out global scope x
    fmt.Printf("x1:%v\n", x)
}

在此示例中,您有 3 个 x 变量。 func 级别一个,{} 中的第一个作用域,然后是 if 块中的另一个(再次独立)。所有这三个都是独立的,两个内部的在定义后(在该范围内)会遮挡外部的,因此即使您选择将 x 2 和 3 作为初始 x 的基础,它们也不会影响其值。

你可以在最后打印出全局范围 x 时看到这一点,并且因为 x3 不受 x2 值的影响,所以我们在函数的末尾结束:

  • x1: 10
  • x2: 11
  • x3: 11