What does this counter() closure print across three calls, and why does the count persist?
Read the snippet below. counter returns a closure that increments a local variable count on each call.
Determine what the three c() calls print, and explain why the count persists across calls.
func counter() func() int {
count := 0
return func() int {
count++
return count
}
}
func main() {
c := counter()
fmt.Println(c(), c(), c())
}
Predict the output.
It prints 1 2 3. The returned function closes over the variable count by reference, not by value, so the single count lives on after counter returns and each call increments the same variable. A second counter() makes an independent count starting again at 1.
- ✗Thinking the closure captures
countby value, resetting it each call - ✗Believing
countis freed whencounterreturns — escape analysis moves it to the heap - ✗Expecting two counters from
counter()to share the samecount
- →Why does escape analysis move
countto the heap here? - →How would the output change if
counterreturned two closures sharing onecount?
What does this code print?
func counter() func() int {
count := 0
return func() int {
count++
return count
}
}
func main() {
c := counter()
fmt.Println(c(), c(), c())
}
Output
1 2 3
Why the count persists
counter declares a local count := 0 and returns a closure that captures it by reference. The closure holds a reference to the same variable, not a copy of it.
Because the variable outlives the function return (escape analysis moves count to the heap), each c() call increments and reads the same count: 1, then 2, then 3.
⚠️ Evaluation-order subtlety: Go evaluates Println's arguments left to right, so c(), c(), c() yields 1 2 3, not the reverse.
Each fresh counter() creates a new variable:
c1 := counter()
c2 := counter()
fmt.Println(c1(), c1(), c2()) // 1 2 1 — c2 is independent