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:
Block
, i.e. else { /* code */ }
IfStmt
again, as in your case, i.e. 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:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With