Go Closures
A closure is an anonymous function that remembers variables from the surrounding scope where it was created. Even after the outer function has finished running, the closure keeps a live reference to those variables and can read or modify them.
Simple Closure Example
package main
import "fmt"
func makeCounter() func() int {
count := 0
return func() int {
count++
return count
}
}
func main() {
counter := makeCounter()
fmt.Println(counter()) // 1
fmt.Println(counter()) // 2
fmt.Println(counter()) // 3
}
Each call to counter() increments and returns the same count variable. The closure holds onto count even though makeCounter has already returned.
Closure Memory Diagram
makeCounter() runs and returns a function.
The returned function carries a reference to count.
┌─────────────────────────────────┐
│ Closure (returned function) │
│ │
│ References: │
│ count → 0 (stored in memory) │
│ │
│ Each call: │
│ count++ │
│ return count │
└─────────────────────────────────┘
counter() → count becomes 1, returns 1
counter() → count becomes 2, returns 2
counter() → count becomes 3, returns 3
Independent Closures
Each call to makeCounter creates a brand new, independent closure with its own separate count variable.
package main
import "fmt"
func makeCounter() func() int {
count := 0
return func() int {
count++
return count
}
}
func main() {
counterA := makeCounter()
counterB := makeCounter()
fmt.Println(counterA()) // 1
fmt.Println(counterA()) // 2
fmt.Println(counterB()) // 1 ← B has its own independent count
fmt.Println(counterA()) // 3
}
Closure as a Multiplier Factory
package main
import "fmt"
func makeMultiplier(factor int) func(int) int {
return func(n int) int {
return n * factor
}
}
func main() {
double := makeMultiplier(2)
triple := makeMultiplier(3)
fmt.Println(double(5)) // 10
fmt.Println(triple(5)) // 15
fmt.Println(double(10)) // 20
}
Closure Modifying an Outer Variable
package main
import "fmt"
func main() {
total := 0
add := func(n int) {
total += n
}
add(10)
add(20)
add(5)
fmt.Println(total) // 35
}
The closure add directly modifies total from the enclosing scope. This is different from passing a copy — the closure holds a reference to the original variable.
Where Closures Are Used
| Use Case | Description |
|---|---|
| Counters and accumulators | Maintain state between calls |
| Factory functions | Generate customized functions |
| Callbacks | Pass behavior with context to other functions |
| Middleware | Wrap logic around HTTP handlers in web servers |
Key Points
- A closure captures variables from its surrounding scope by reference
- The captured variables stay alive as long as the closure exists
- Each closure call that creates a new function gets its own independent captured variables
- Closures are commonly used to maintain state without global variables
