Anonymous Functions and Closures
Anonymous functions and closures are powerful features in Go that enable functional programming patterns and elegant code organization.
Anonymous Functions
Basic Anonymous Function
package main
import "fmt"
func main() {
// Anonymous function
func() {
fmt.Println("Hello from anonymous function")
}()
// Anonymous function with parameters
func(name string) {
fmt.Printf("Hello, %s!\n", name)
}("Alice")
// Anonymous function with return value
result := func(a, b int) int {
return a + b
}(5, 3)
fmt.Println("Result:", result)
}
Assigning to Variables
package main
import "fmt"
func main() {
// Assign anonymous function to variable
greet := func(name string) string {
return fmt.Sprintf("Hello, %s!", name)
}
fmt.Println(greet("Alice"))
fmt.Println(greet("Bob"))
}
Closures
Basic Closure
package main
import "fmt"
func main() {
x := 10
// Closure captures x
f := func() {
fmt.Println("x is", x)
}
f() // x is 10
x = 20
f() // x is 20
}
Closure with State
package main
import "fmt"
func counter() func() int {
count := 0
return func() int {
count++
return count
}
}
func main() {
c1 := counter()
fmt.Println(c1()) // 1
fmt.Println(c1()) // 2
fmt.Println(c1()) // 3
c2 := counter()
fmt.Println(c2()) // 1 (separate closure)
}
Closure Modifying Captured Variables
package main
import "fmt"
func makeAdder(x int) func(int) int {
return func(y int) int {
x += y
return x
}
}
func main() {
add5 := makeAdder(5)
fmt.Println(add5(3)) // 8
fmt.Println(add5(2)) // 10
fmt.Println(add5(1)) // 11
}
Functional Patterns
Map Function
package main
import "fmt"
func mapInts(nums []int, f func(int) int) []int {
result := make([]int, len(nums))
for i, n := range nums {
result[i] = f(n)
}
return result
}
func main() {
nums := []int{1, 2, 3, 4, 5}
// Double each number
doubled := mapInts(nums, func(n int) int {
return n * 2
})
fmt.Println(doubled) // [2 4 6 8 10]
// Square each number
squared := mapInts(nums, func(n int) int {
return n * n
})
fmt.Println(squared) // [1 4 9 16 25]
}
Filter Function
package main
import "fmt"
func filterInts(nums []int, f func(int) bool) []int {
var result []int
for _, n := range nums {
if f(n) {
result = append(result, n)
}
}
return result
}
func main() {
nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
// Filter even numbers
evens := filterInts(nums, func(n int) bool {
return n%2 == 0
})
fmt.Println(evens) // [2 4 6 8 10]
// Filter numbers > 5
large := filterInts(nums, func(n int) bool {
return n > 5
})
fmt.Println(large) // [6 7 8 9 10]
}
Reduce Function
package main
import "fmt"
func reduceInts(nums []int, initial int, f func(int, int) int) int {
result := initial
for _, n := range nums {
result = f(result, n)
}
return result
}
func main() {
nums := []int{1, 2, 3, 4, 5}
// Sum
sum := reduceInts(nums, 0, func(acc, n int) int {
return acc + n
})
fmt.Println("Sum:", sum) // 15
// Product
product := reduceInts(nums, 1, func(acc, n int) int {
return acc * n
})
fmt.Println("Product:", product) // 120
}
Defer with Closures
Cleanup with Closure
package main
import "fmt"
func processFile(filename string) {
file := openFile(filename)
defer func() {
fmt.Println("Closing file:", filename)
file.Close()
}()
fmt.Println("Processing file:", filename)
}
type File struct {
name string
}
func (f *File) Close() {
fmt.Printf("File %s closed\n", f.name)
}
func openFile(name string) *File {
fmt.Printf("File %s opened\n", name)
return &File{name: name}
}
func main() {
processFile("data.txt")
}
Closure Gotchas
Loop Variable Capture
package main
import "fmt"
func main() {
// Common mistake
var funcs []func()
for i := 0; i < 3; i++ {
funcs = append(funcs, func() {
fmt.Println(i) // All print 3
})
}
for _, f := range funcs {
f()
}
// Output: 3 3 3
}
Correct Loop Variable Capture
package main
import "fmt"
func main() {
// Correct approach
var funcs []func()
for i := 0; i < 3; i++ {
i := i // Create new variable in each iteration
funcs = append(funcs, func() {
fmt.Println(i)
})
}
for _, f := range funcs {
f()
}
// Output: 0 1 2
}
Using Function Parameter
package main
import "fmt"
func main() {
// Best approach
var funcs []func()
for i := 0; i < 3; i++ {
funcs = append(funcs, func(n int) func() {
return func() {
fmt.Println(n)
}
}(i))
}
for _, f := range funcs {
f()
}
// Output: 0 1 2
}
Best Practices
โ Good: Use Closures for State
// DO: Use closures to encapsulate state
func newCounter() func() int {
count := 0
return func() int {
count++
return count
}
}
โ Good: Use Anonymous Functions for Callbacks
// DO: Use anonymous functions for callbacks
go func() {
fmt.Println("Running in goroutine")
}()
โ Bad: Capture Loop Variables Incorrectly
// DON'T: Capture loop variables without care
for i := 0; i < 3; i++ {
go func() {
fmt.Println(i) // All print 3
}()
}
โ Good: Document Closure Behavior
// DO: Document what variables are captured
// makeMultiplier returns a function that multiplies by factor
func makeMultiplier(factor int) func(int) int {
return func(x int) int {
return x * factor
}
}
Summary
Anonymous functions and closures provide:
- Function literals for inline functions
- Closures for capturing variables
- Functional patterns like map, filter, reduce
- State encapsulation without classes
- Elegant callbacks and handlers
These features enable powerful functional programming in Go.
Comments