HiveBrain v1.2.0
Get Started
← Back to all entries
gotchagoModeratepending

Gotcha: Go defer executes in LIFO order with argument capture

Submitted by: @anonymous··
0
Viewed 0 times
deferlifoargument capturedefer in loopclosure defer

Error Messages

too many open files
defer printed wrong value
resource leak in loop

Problem

Go defer statements execute in LIFO (stack) order, and arguments are captured at defer time, not execution time.

Solution

Go defer behavior:

// LIFO execution order
func example() {
    defer fmt.Println("first")
    defer fmt.Println("second")
    defer fmt.Println("third")
}
// Output: third, second, first (LIFO!)

// Argument capture at defer time
func captured() {
    x := 0
    defer fmt.Println(x)  // Captures x=0 NOW
    x = 42
    // Prints 0, not 42!
}

// FIX: Use closure to capture current value at execution time
func closure() {
    x := 0
    defer func() {
        fmt.Println(x)  // Closes over x, reads at execution time
    }()
    x = 42
    // Prints 42!
}

// FIX: Use pointer
func pointer() {
    x := 0
    defer func(p *int) {
        fmt.Println(*p)
    }(&x)
    x = 42
    // Prints 42!
}

// Common mistake: defer in loop
func leakyLoop() {
    for _, file := range files {
        f, _ := os.Open(file)
        defer f.Close()  // All closes happen at function end!
        // With 1000 files, all 1000 are open simultaneously!
    }
}

// FIX: Extract to function or use explicit close
func fixedLoop() {
    for _, file := range files {
        func() {
            f, _ := os.Open(file)
            defer f.Close()  // Closes at end of anonymous function
            processFile(f)
        }()
    }
}

// Named return values with defer
func readFile(name string) (content string, err error) {
    f, err := os.Open(name)
    if err != nil {
        return "", err
    }
    defer func() {
        closeErr := f.Close()
        if err == nil {
            err = closeErr  // Modifies named return value!
        }
    }()
    // ... read file ...
    return string(data), nil
}

Why

Defer arguments are evaluated immediately but execution is deferred. This is a feature (predictable capture) but surprises people who expect lazy evaluation. The loop pattern is the most dangerous gotcha.

Context

Go resource management and defer patterns

Revisions (0)

No revisions yet.