Pebellslearn by building
0%
RoadmapsMatrixCoursesSandboxSign in
Variables & Constants  ›  Capstone Project

Scope and Shadowing

Scope and Shadowing 14 minA name is visible from its declaration to the end of the enclosing block, and declaring the same name in an inner block creates a new variable that hides the outer one.

Scope and Shadowing

Here is a bug nearly every Go programmer writes once. tally counts the words, prints the total — and prints 0. The counting worked; the program just printed a different variable with the same name.

The rule

A name lives until its closing brace; an inner := makes a new one.

Scope is the region of code where a name can be used. In Go it follows the braces:

  • Package scope: declared outside any function, visible in every file of the package.
  • Function scope: parameters and variables declared in a function body.
  • Block scope: variables declared inside { } of an if, for, switch or a bare block, plus variables declared in the header of those statements, such as if err := check(); err != nil.

A name is visible from where it is declared to the end of its block, including all the blocks nested inside.

Shadowing happens when an inner block declares the same name again. The inner variable hides the outer one until the inner block ends:

total := 0
if len(words) > 0 {
	total := len(words) // new variable, only inside this block
	fmt.Println(total)  // 5
}
fmt.Println(total) // 0

Nothing is wrong with the syntax, so the compiler is happy. The fix is a single character: delete the colon, so the line reads total = len(words).

Short-lived scope is also why the if header form is so handy:

if n, err := countFile(path); err != nil {
	return err
}
// n and err do not exist here

The variables vanish after the if, so they cannot clash with anything later.

The shortcut

When a value "didn't change", find every := of that name and draw its braces. If the one you assigned to lives in a smaller box than the one you print, you shadowed it.

Hunting the zero totalStep 1 of 4
  1. Find the declarations

    total := appears twice: once in main, once inside the if.

Two more places the same trap hides: a := inside a for loop body, and err redeclared inside a nested block so the outer err is never set. Linters, such as the shadow analyzer, can flag these.

Drills

Answer fast — trust the rule.

quick drill

What does x := 1; { x := 2; fmt.Print(x) }; fmt.Print(x) print?

quick drill

x := 1; { x = 2 }; fmt.Println(x) — what prints?

quick drill

After if n, err := count(); err == nil { ... }, can the next line use n?

quick drill

Why does the compiler not stop the shadowing bug by itself?

Remember

  • A name lives until its closing brace; an inner := makes a new one.
  • Shortcut: find every := of the name and draw its braces.
  • Trap: an inner := "updates" nothing — use = to change the outer variable.

Practice — level up

0/1 solved

Write your answer, then compare it with a rubric and a model answer.

ExplainThe total that stayed zero

In tally, total := 0 is declared in main. Inside an if len(words) > 0 { total := len(words); fmt.Println(total) } block the count is set, but after the block total prints 0. Explain exactly why, and give the one-character fix.