简体   繁体   中英

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?

The following code is expected to fail with an error on compilation:

package main

import (
    "fmt"
)

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

The compilation error is:

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

So I was expecting this code to also fail with error:

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)
    }
}

Here is the output:

else if block: x:  11

Why does the second program succeed even though the := operator in else if x:= x + 1 is not defining any new variable?

From the Go specs, here is how an if statement is defined:

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

Later on, in the Declarations and Scope sections it is said:

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.

Now, the if statement is an implicit block :

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

Then as you can see from the IfStmt definition, after the keyword else may come:

  • a Block , ie else { /* code */ }
  • an IfStmt again, as in your case, ie else if /* statement */ { /* code */ } . This means the IfStmt is recursive, and it is an implicit block within another IfStmt (still an implicit block). Therefore it meets the condition for redeclaring the identifier.

Compare also with explicit blocks:

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

You're dealing with two different x variables due to your second x:= being in another scope. So it looks like the same x to you, but it's not, even though it is based off the value of the outer x, it doesn't affect it. The x you are printing is the inner x.

Consider this 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)
}

In this example you have 3 x variables. The func level one, the first scoped one within {}, then another one (independent again) within the if block. All three of these are independent, and the two inner ones shadow the outer one after they are defined (within that scope), so even if you choose to base x 2 and 3 off the initial x, they don't impact its value.

You can see this as you print out the global scope x at the end, and because x3 is not affected by the value of x2, so we end up at the end of the function:

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

In this example,

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)
    }
}

you define x variable at two places which is correct because scope of that variable is under if and else. Consider this code like this

在此处输入图像描述

If you see here we have two block, block 1 and block 2

Both block have their own scope, what ever you define inside that block will not accessible by out side.

Try this, and you will get an error.

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)
}

error: ./prog.go:13:45: undefined: x

Because you are trying to access that variable outside.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM