Scope and shadowing
Each kitchen appliance has its own drawer of food. The drawer holds boxes, and the boxes hold smaller jars:
- a jar placed in the drawer can be seen from any box inside it;
- a jar inside a box cannot be seen from outside the box;
- a neighboring appliance does not look into someone else's drawer.
Variables in Go work the same way: where a variable is declared is where it can be seen.
Sometimes this gives a surprising result. Inside the if the code says x := 10, but after the block x equals 1 again. The compiler reports no error, because the program has two different variables named x:
package main
import "fmt"
func main() {
x := 1
if x > 0 {
x := 10
fmt.Println("inside:", x) // inside: 10
}
fmt.Println("outside:", x) // outside: 1
}Blocks and scopes
A block is code in curly braces: a function body, an if branch, a loop body. Where a name is visible depends on where it was declared. Let's go through four cases.
A variable inside a block
The scope of such a variable starts at its declaration and ends at the closing brace of the block:
- it is visible in nested blocks;
- it does not exist outside the block: if
ywas declared inside anif, usingyafter the block fails to compile withundefined: y; - it does not exist above its declaration either, even in the same block.
A variable from a for or if header
A variable from a for header and a variable from an if initializer belong to the block of their statement. The variable n from if n := 3; n > 5 is visible in both branches, else included. After the statement these variables are gone.
Function parameters
Parameters are visible throughout the body of their function. The variables of two different functions cannot see each other: each appliance has its own drawer.
Package-level names
A name declared outside functions, with var or const, is visible in the whole package. Any function can read it, even one written above the declaration.
Shadowing
The := operator declares a new variable in the current block, even if a variable with the same name already exists outside. The inner variable hides the outer one until the end of its block, and this is called shadowing.
Picture a new jar with the same label placed in the box: while you are in the box, your hand reaches for the nearer one. The outer variable does not change, and after the block the name means it again.
Let's walk through the first program line by line:
x := 1creates the outerx.- The condition
x > 0is true, andx := 10creates a secondxinside theif. - The first
Printlnprints that one:inside: 10. - After the closing brace the inner
xis gone, and the secondPrintlnprints the outer one:outside: 1.
This is where the jar analogy ends. From a box you can reach past to the farther jar, but in Go, until the block ends, the name x means only the inner variable. The outer one can no longer be reached by that name.
Shadowing is not an error, but such code is hard to read, so it is better to give the inner variable a different name.
Interviewers like to ask this as a "what does the program print" puzzle with := inside an if. Look for the block each variable is declared in: := in a nested block creates a new one, and the outer one stays as it was.
:= or =
In a nested block, the two operators with the same name x do different things.
:= makes a new variable
:= declares a variable in the current block. In a nested block that is shadowing, as in the first program. Declaring a name twice this way in one block is not allowed: the compiler answers no new variables on left side of :=.
= uses the same variable
= declares nothing; it looks the name up in the current block and outside it. So to change the outer variable, write an assignment inside the block:
x := 1
if x > 0 {
x = 10
}
fmt.Println(x) // 10The if has no x of its own, so x = 10 changes the outer one. After the block the program prints 10.
Common mistake
A sum in a loop is accumulated with :=:
total := 0
for i := 1; i <= 3; i++ {
total := total + i // ✗
fmt.Print(total, " ")
}
fmt.Println(total)The program prints 1 2 3 0. On each iteration := creates a new total from the outer one, which stays 0. Write total += i or total = total + i.
Key points
- A variable is visible from its declaration to the closing brace of its block, nested blocks included.
:=in a nested block declares a new variable that shadows the outer one with the same name.- To change the outer variable inside a block, write
=, not:=.
Subscribe to take the course
The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.