Skip to content

Go Closures

A closure in Go is a function value that captures and references variables from its surrounding lexical scope. This means the inner function maintains a live reference to those variables, allowing it to read and modify them even after the outer function has returned.

Basic Example

Closures enable functions to maintain persistent state across multiple calls, creating "memory" without globals.

package main

import "fmt"

func intSeq() func() int {
    i := 0
    return func() int {
        i++
        return io
    }
}

func main() {
    // nextInt is a closure that "captures" the variable i
    nextInt := intSeq()

    fmt.Println(nextInt()) // 1
    fmt.Println(nextInt()) // 2
    fmt.Println(nextInt()) // 3

    // To confirm that the state is unique to that function instance:
    newInts := intSeq()
    fmt.Println(newInts()) // 1
}

Why use Closures?

  1. State Management: They allow functions to maintain state without using global variables.
  2. Function Factories: You can create specialized functions on the fly.
  3. Callbacks: Frequently used in asynchronous code or when passing logic to other functions.

Common Pitfall: Loop Variables

When creating closures inside a loop, they all share the same loop variable if you aren't careful.

1
2
3
4
5
6
// Be careful with this pattern
for i := 0; i < 3; i++ {
    go func() {
        fmt.Println(i) // Might print 3, 3, 3
    }()
}

To fix this, pass the variable as a parameter or redefine it inside the loop:

1
2
3
4
5
6
for i := 0; i < 3; i++ {
    i := i // Create a unique instance for each closure
    go func() {
        fmt.Println(i)
    }()
}