gotchagoModeratepending
Gotcha: Go defer executes in LIFO order with argument capture
Viewed 0 times
deferlifoargument capturedefer in loopclosure defer
Error Messages
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.