Go by Example

Closures

Functions that capture variables from their surrounding scope, including closures used by goroutines.

A closure is a function that uses a variable from the scope where it was created. It keeps access to that variable after the outer function returns.

Each call to this factory creates a counter with its own captured value:

package main
 
import "fmt"
 
func makeCounter() func() int {
    count := 0
    return func() int {
        count++
        return count
    }
}
 
func main() {
    counter := makeCounter()
    fmt.Println(counter(), counter()) // 1 2
}

A goroutine can receive the loop value as an argument. This makes a copy at the call site. All three values print, but their order may vary:

package main
 
import (
    "fmt"
    "sync"
)
 
func main() {
    var wg sync.WaitGroup
    for i := 0; i < 3; i++ {
        wg.Add(1)
        go func(n int) {
            defer wg.Done()
            fmt.Println(n)
        }(i)
    }
    wg.Wait()
}

In production

The module's go directive controls loop-variable semantics. Modules declaring Go 1.22 or later get a new variable for each iteration; modules declaring an older version reuse the loop variable. Passing it as an argument works in both cases.

Enjoyed this? Get more software craft delivered to your inbox.