gotchagoMajor
Defer in a loop does not execute until the function returns
Viewed 0 times
defer loopdefer in for loopresource leakfile not closeddeferred callloop defer gotcha
Problem
Placing defer inside a for loop defers the call until the enclosing function returns, not until the end of each loop iteration. Files or locks acquired in the loop body are held for the entire function lifetime.
Solution
Wrap the loop body in an anonymous function so defer fires at the end of each iteration:
// WRONG — all files stay open until openAll() returns
func openAll(paths []string) {
for _, p := range paths {
f, _ := os.Open(p)
defer f.Close() // deferred to function exit, not iteration end
process(f)
}
}
// RIGHT — file closed after each iteration
func openAll(paths []string) {
for _, p := range paths {
func() {
f, _ := os.Open(p)
defer f.Close()
process(f)
}()
}
}Why
defer pushes a call onto the current function's deferred call stack, not the loop's. The stack is flushed in LIFO order when the function returns or panics.
Gotchas
- This is especially dangerous with database transactions — the tx.Rollback() may not fire until long after you intend
- The anonymous function approach has a slight overhead from closure allocation; for tight loops consider explicit Close()
- Defer inside a goroutine flushes when that goroutine's function returns, not when the parent function returns
Revisions (0)
No revisions yet.