Go Closures

Anonymous functions that capture variables from their environment

🔒 What are Go Closures?

Closures are anonymous functions that can access variables from their surrounding scope. They "close over" variables, maintaining access to them even after the outer function returns.


// Function returning a closure
func counter() func() int {
    count := 0
    return func() int {
        count++
        return count
    }
}

func main() {
    increment := counter()
    fmt.Println(increment()) // 1
    fmt.Println(increment()) // 2
}
                                    

Output:

1

2

Key Closure Concepts

🎭

Anonymous

Functions without names

func() {
    fmt.Println("Anonymous")
}()
🔗

Variable Capture

Access outer scope variables

x := 10
func() {
    fmt.Println(x) // Captures x
}()
💾

State Persistence

Variables persist between calls

// count persists across calls
🏭

Function Factory

Create specialized functions

adder := makeAdder(5)
result := adder(3) // 8

🔹 Basic Closure Examples

Simple closures to understand the concept:

package main

import "fmt"

func main() {
    // Simple anonymous function
    func() {
        fmt.Println("Hello from anonymous function!")
    }()
    
    // Closure capturing variable
    message := "Hello from closure"
    greet := func() {
        fmt.Println(message)
    }
    greet()
    
    // Closure with parameters
    name := "Alice"
    sayHello := func(greeting string) {
        fmt.Printf("%s, %s!\n", greeting, name)
    }
    sayHello("Hi")
    sayHello("Good morning")
}

Output:

Hello from anonymous function!

Hello from closure

Hi, Alice!

Good morning, Alice!

🔹 Function Factories

Functions that return customized closures:

package main

import "fmt"

// Create adder function
func makeAdder(x int) func(int) int {
    return func(y int) int {
        return x + y
    }
}

// Create multiplier function
func makeMultiplier(factor int) func(int) int {
    return func(n int) int {
        return n * factor
    }
}

// Create greeting function
func makeGreeter(greeting string) func(string) {
    return func(name string) {
        fmt.Printf("%s, %s!\n", greeting, name)
    }
}

func main() {
    // Create specialized adders
    add5 := makeAdder(5)
    add10 := makeAdder(10)
    
    fmt.Println("5 + 3 =", add5(3))
    fmt.Println("10 + 7 =", add10(7))
    
    // Create multipliers
    double := makeMultiplier(2)
    triple := makeMultiplier(3)
    
    fmt.Println("Double 6:", double(6))
    fmt.Println("Triple 4:", triple(4))
    
    // Create greeters
    englishGreet := makeGreeter("Hello")
    spanishGreet := makeGreeter("Hola")
    
    englishGreet("Bob")
    spanishGreet("Carlos")
}

Output:

5 + 3 = 8

10 + 7 = 17

Double 6: 12

Triple 4: 12

Hello, Bob!

Hola, Carlos!

🔹 Stateful Closures

Closures that maintain state between calls:

package main

import "fmt"

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

// Bank account closure
func makeBankAccount(initialBalance float64) (func(float64), func(float64), func() float64) {
    balance := initialBalance
    
    deposit := func(amount float64) {
        balance += amount
        fmt.Printf("Deposited $%.2f. Balance: $%.2f\n", amount, balance)
    }
    
    withdraw := func(amount float64) {
        if amount <= balance {
            balance -= amount
            fmt.Printf("Withdrew $%.2f. Balance: $%.2f\n", amount, balance)
        } else {
            fmt.Println("Insufficient funds!")
        }
    }
    
    getBalance := func() float64 {
        return balance
    }
    
    return deposit, withdraw, getBalance
}

func main() {
    // Counter example
    counter1 := makeCounter()
    counter2 := makeCounter()
    
    fmt.Println("Counter 1:", counter1()) // 1
    fmt.Println("Counter 1:", counter1()) // 2
    fmt.Println("Counter 2:", counter2()) // 1 (independent)
    
    fmt.Println("---")
    
    // Bank account example
    deposit, withdraw, getBalance := makeBankAccount(100.0)
    
    fmt.Printf("Initial balance: $%.2f\n", getBalance())
    deposit(50.0)
    withdraw(30.0)
    withdraw(200.0) // Should fail
}

Output:

Counter 1: 1

Counter 1: 2

Counter 2: 1

---

Initial balance: $100.00

Deposited $50.00. Balance: $150.00

Withdrew $30.00. Balance: $120.00

Insufficient funds!

🧠 Test Your Knowledge

What makes a closure different from a regular function?