Anonymous functions
A function literal is a function without a name. It is an expression, so it can go anywhere a value can: a variable, an argument, a return value, a struct field.
Go has no arrow or lambda shorthand. The full func(params) results { body } form is the only way to write one.
Capturing variables
A function literal can use variables from the enclosing function. It does not copy them: the closure and the surrounding code share one variable.
The captured variable lives as long as some closure still refers to it, even after the function that declared it has returned. The compiler moves it to the heap when needed; you never manage that yourself.
Generators and counters
Returning a closure from a function gives each returned function its own private state.
Each call to counter() creates a new n, so c1 and c2 count independently. Nothing outside can read or reset n, which makes this a small form of encapsulation.
Closures in loops (Go 1.22 and later)
Before Go 1.22, a for loop had one loop variable for the whole loop. Closures created inside it all captured that single variable and saw its final value. That was the most common closure bug in Go.
Since Go 1.22, each iteration gets its own copy, so the code below prints 0 1 2:
Under Go 1.21 and earlier, the same loop written as for i := 0; i < 3; i++ (range over an integer is also new in 1.22) printed 3 3 3. The new rule applies per module, based on the go line in go.mod, so an old module still gets the old behavior until its go line is raised to 1.22 or later. You will still see i := i or v := v inside loops in older code: it was the manual fix and is now redundant.
The per-iteration rule covers only variables declared by the for statement itself. A variable declared before the loop and updated in the body is still one shared variable:
var last string
for _, s := range items {
last = s
handlers = append(handlers, func() { use(last) }) // every closure sees the final value
}
Closures and goroutines
A goroutine started with a function literal is a closure too. The same sharing rules apply, plus the usual concurrency rule: if several goroutines write a captured variable, you need a mutex or a channel.
This always prints 5050. Each goroutine reads its own i (Go 1.22 loop semantics) and writes the shared total under a lock. Remove the mutex and the result becomes unpredictable; go run -race reports it as a data race. See goroutines for WaitGroup and the race detector.
Where closures show up in real code
- Sorting and searching:
slices.SortFunc,slices.IndexFunc,sort.Sliceall take a function literal. - Deferred cleanup:
defer func() { ... }()runs a block at function exit and can read the function's variables. See defer. - HTTP middleware: a function that takes a handler and returns a new
http.HandlerFuncliteral that wraps it. - Configuration: functional options (
func WithTimeout(d time.Duration) Option { return func(c *Config) { c.Timeout = d } }) are closures over the argument.
A middleware example that captures both a parameter and a counter:
Common mistakes
- Expecting a snapshot. A closure reads the variable's current value when it runs, not the value when the closure was created. Pass the value as a parameter if you need a snapshot:
go func(v int) { ... }(x). - Recursive literals. A function literal cannot refer to itself by the variable it is being assigned to in the same
:=. Declare the variable first:var walk func(n int); walk = func(n int) { ... walk(n-1) }. - Unsynchronized writes from goroutines. Capturing is not synchronization. Guard shared writes.
Frequently Asked Questions
What is an anonymous function in Go?
A function literal without a name: func(x int) int { return x * 2 }. You can assign it to a variable, pass it as an argument, return it, or call it immediately by adding () after the closing brace. Go has no separate lambda syntax; a function literal is the lambda.
What is a closure in Go?
A function literal that refers to variables declared outside it. The function keeps those variables alive and shares them with the enclosing scope, so changes made inside the closure are visible outside and the other way round. Variables are captured by reference, not copied.
Do Go closures capture variables by value or by reference?
By reference. The closure and the surrounding code use the same variable. If you need a snapshot, copy the value into a new variable before creating the closure, or pass it as an argument.
Is the closure loop variable bug fixed in Go?
Yes, since Go 1.22. Each iteration of a for loop now declares a fresh loop variable, so closures and goroutines created in the loop see that iteration's value. It applies to modules whose go.mod says go 1.22 or later. Older code often has i := i inside the loop as a workaround; it is harmless but no longer needed.