Go Closures

A closure is an anonymous function that remembers variables from the surrounding scope where it was created. Even after the outer function has finished running, the closure keeps a live reference to those variables and can read or modify them.

Simple Closure Example

package main

import "fmt"

func makeCounter() func() int {
    count := 0
    return func() int {
        count++
        return count
    }
}

func main() {
    counter := makeCounter()

    fmt.Println(counter()) // 1
    fmt.Println(counter()) // 2
    fmt.Println(counter()) // 3
}

Each call to counter() increments and returns the same count variable. The closure holds onto count even though makeCounter has already returned.

Closure Memory Diagram

makeCounter() runs and returns a function.
The returned function carries a reference to count.

┌─────────────────────────────────┐
│  Closure (returned function)    │
│                                 │
│  References:                    │
│    count → 0 (stored in memory) │
│                                 │
│  Each call:                     │
│    count++                      │
│    return count                 │
└─────────────────────────────────┘

counter()  → count becomes 1, returns 1
counter()  → count becomes 2, returns 2
counter()  → count becomes 3, returns 3

Independent Closures

Each call to makeCounter creates a brand new, independent closure with its own separate count variable.

package main

import "fmt"

func makeCounter() func() int {
    count := 0
    return func() int {
        count++
        return count
    }
}

func main() {
    counterA := makeCounter()
    counterB := makeCounter()

    fmt.Println(counterA()) // 1
    fmt.Println(counterA()) // 2
    fmt.Println(counterB()) // 1  ← B has its own independent count
    fmt.Println(counterA()) // 3
}

Closure as a Multiplier Factory

package main

import "fmt"

func makeMultiplier(factor int) func(int) int {
    return func(n int) int {
        return n * factor
    }
}

func main() {
    double := makeMultiplier(2)
    triple := makeMultiplier(3)

    fmt.Println(double(5))  // 10
    fmt.Println(triple(5))  // 15
    fmt.Println(double(10)) // 20
}

Closure Modifying an Outer Variable

package main

import "fmt"

func main() {
    total := 0

    add := func(n int) {
        total += n
    }

    add(10)
    add(20)
    add(5)

    fmt.Println(total) // 35
}

The closure add directly modifies total from the enclosing scope. This is different from passing a copy — the closure holds a reference to the original variable.

Where Closures Are Used

Use CaseDescription
Counters and accumulatorsMaintain state between calls
Factory functionsGenerate customized functions
CallbacksPass behavior with context to other functions
MiddlewareWrap logic around HTTP handlers in web servers

Key Points

  • A closure captures variables from its surrounding scope by reference
  • The captured variables stay alive as long as the closure exists
  • Each closure call that creates a new function gets its own independent captured variables
  • Closures are commonly used to maintain state without global variables

Leave a Comment