In Go, the rules of variable scoping are strictly defined by blocks ({}), and a variable name can be shadowed in nested scopes. Many traps occur especially in nested functions, anonymous functions, loops, and when declaring variables with the same name at different levels.
Go has specifically minimized "magical" behavior concerning scope to make the code more readable. However, the flexibility of the syntax and the allowance for redeclaring variables through the short form := often leads to misunderstandings.
If a variable is declared in a nested function or within a loop block with the same name as at the higher level, the outer variable becomes inaccessible (shadowed). In most cases, this does not get noticed by the compiler and can easily lead to errors, especially when working with closures. Another common mistake is declaring a new variable in an if block or in a for-init, and then attempting to access it outside the block.
Always keep an eye on the levels of scope. Do not use the same variable names in nested blocks or anonymous functions without real necessity, avoid short names, and be careful with the usage of :=.
Example code:
package main import "fmt" func main() { x := 1 { x := 2 // shadows x from main() fmt.Println("Inner x:", x) } fmt.Println("Outer x:", x) for i := 0; i < 3; i++ { x := i // a new x is created on each iteration go func() { fmt.Println("Goroutine x:", x) }() } }
In this example, the outer variable x is not modified; a new x is created within the block. In the second loop, the variable x is captured in the nested function — the result may be unexpected.
Key features:
:= inside a block always creates a new variable, even if an outer one already exists.1. What value of the variable will be printed last in the nested block when shadowed?
The value of the outer variable, because the inner variable exists only within the block.
2. What will happen if you try to access a variable declared inside an if/for block outside that block?
The compiler will throw an error: the variable is out of scope.
if true { y := 5 } fmt.Println(y) // error
3. How to avoid unexpected values when creating goroutines in a loop using a variable?
Pass the variable as a parameter to the function:
for i := 0; i < 3; i++ { go func(val int) { fmt.Println(val) }(i) }
:= will modify an existing variable (it will create a new one).A loop initializes several goroutines for parallel processing, but the loop variable is used inside a closure without passing it — all goroutines operate with its "last" value.
Pros:
Cons:
Passing the loop variable as a parameter to the closure — each goroutine receives its value.
Pros:
Cons: