A closure is a function that references variables from outside its own body. The function can access and modify these variables even after the outer function has returned. Closures are powerful tools for creating stateful functions, callbacks, and elegant APIs.
A closure is an anonymous function that “closes over” variables from its surrounding scope:
func main() { min := 3 // This is a closure - it references 'min' from the outer scope greaterThan := func(n int) bool { return n > min } result := greaterThan(5) // true result = greaterThan(2) // false}
The greaterThan function “remembers” the min variable and can access it every time it’s called.
Closures (Anonymous Functions with Captured Variables)
Closures are anonymous functions that capture variables from their environment:
multiplier := 10// Closure that captures 'multiplier'multiply := func(x int) int { return x * multiplier}result := multiply(5) // 50
All closures are anonymous functions, but not all anonymous functions are closures. A function only becomes a closure when it references variables from outside its body.
Here’s a real-world example showing the power of closures:
type filterFunc func(int) boolfunc main() { nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} // Using regular functions fmt.Println("••• FUNC VALUES •••") fmt.Printf("evens : %d\n", filter(isEven, nums...)) fmt.Printf("odds : %d\n", filter(isOdd, nums...)) // Using closures for dynamic behavior fmt.Println("\n••• CLOSURES •••") var min int greaterThan := func(n int) bool { return n > min } min = 3 fmt.Printf("> 3 : %d\n", filter(greaterThan, nums...)) // Output: [4 5 6 7 8 9 10] min = 6 fmt.Printf("> 6 : %d\n", filter(greaterThan, nums...)) // Output: [7 8 9 10]}func filter(f filterFunc, nums ...int) (filtered []int) { for _, n := range nums { if f(n) { filtered = append(filtered, n) } } return}func isEven(n int) bool { return n%2 == 0 }func isOdd(m int) bool { return m%2 == 1 }
Closures allow you to create reusable functions with dynamic behavior. Instead of writing greaterThan3 and greaterThan6 as separate functions, you can use one closure with a variable threshold.
// WRONG: All closures reference the same variablevar functions []func() intfor i := 0; i < 3; i++ { functions = append(functions, func() int { return i // Captures the loop variable })}for _, f := range functions { fmt.Println(f()) // Output: 3, 3, 3 (not 0, 1, 2!)}
// CORRECT: Each closure captures its own variablevar functions []func() intfor i := 0; i < 3; i++ { current := i // Create a new variable for each iteration functions = append(functions, func() int { return current })}for _, f := range functions { fmt.Println(f()) // Output: 0, 1, 2}
Always create a new variable inside the loop when capturing loop variables in closures. Otherwise, all closures will share the same variable and produce unexpected results.
func fibonacci() func() int { a, b := 0, 1 return func() int { result := a a, b = b, a+b return result }}// Usagefib := fibonacci()for i := 0; i < 10; i++ { fmt.Print(fib(), " ")}// Output: 0 1 1 2 3 5 8 13 21 34
Closures keep references to captured variables, which prevents them from being garbage collected:
func createClosure() func() int { // This large slice will stay in memory as long as // the returned closure exists largeData := make([]int, 1000000) counter := 0 return func() int { // Closure keeps largeData alive counter++ return len(largeData) + counter }}closure := createClosure() // largeData stays in memory
Be mindful of what variables your closures capture. Capturing large data structures can lead to unexpected memory usage.