Closures
A closure is a function value that references variables from outside its own body. When a function is defined inside another function, it "closes over" the variables in its surrounding scope โ those variables become part of the closure's environment and remain accessible even after the outer function has returned.
Capture by Reference, Not by Value
This is the most important thing to understand about closures in Go: captured variables are shared, not copied. The closure holds a reference to the variable itself, so mutations are visible to everyone who holds that reference.
This sharing is intentional and powerful โ it is what makes stateful closures work.
Function Factories
A function factory is a function that constructs and returns another function. The returned function closes over the factory's parameters, creating a specialized version of a general behavior.
Each call to makeMultiplier produces a closure that remembers its own copy of n. double and triple are independent โ they do not share state with each other.
Stateful Closures
Because closures capture variables by reference, they can maintain private mutable state across calls without needing a struct or global variable. This is a lightweight alternative when the state is simple.
Each call to makeCounter allocates a new count variable. The returned closure is the only thing that can touch it โ effectively giving you encapsulated state with zero boilerplate.
Higher-Order Functions
A higher-order function either accepts a function as an argument, returns a function, or both. Closures are the natural building block for higher-order patterns.
The inline func literals passed to apply are closures โ they can reference variables from main if needed.
Real-World Uses
HTTP middleware โ wrapping a handler to add logging, authentication, or tracing:
Sort callbacks โ providing a comparison function to sort.Slice:
Deferred cleanup โ closures in defer statements capture the variables they need at definition time, so cleanup logic always refers to the right values:
The Loop Variable Capture Gotcha
This is one of the most common bugs in Go code involving goroutines or deferred functions. The loop variable i is a single variable that is reused each iteration. If a closure captures i directly, all closures end up sharing the same variable โ which has already advanced to its final value by the time they run.
Fix 1 โ shadow with a new variable inside the loop:
Fix 2 โ pass i as an argument to the goroutine function:
Both fixes create a new binding for each iteration. Fix 2 is often preferred for goroutines because it makes the data flow explicit.
Note: Go 1.22 changed loop semantics so each iteration creates a new loop variable automatically โ but understanding the classic gotcha remains essential for reading existing code and for goroutines in older codebases.
Knowledge Check
What does a closure capture?
Which pattern correctly fixes loop variable capture?
What does a function factory return?