RSTR-PERF-301 — defer inside a for loop
Summary
Go's defer runs at function return, not loop iteration. A defer
inside a for body accumulates on the function's defer stack —
every iteration pushes a new entry, and nothing pops them until the
enclosing function returns. For long-running loops over file handles
or DB rows, that means resources stay held for the whole loop, not
just the iteration that opened them.
Severity
Medium. Resource exhaustion (file descriptors, DB connections) is
the usual failure mode.
Languages
Go.
What rastray flags
for _, path := range paths {
f, err := os.Open(path)
if err != nil { return err }
defer f.Close() // ← flagged
...
}
What rastray deliberately does not flag
deferat function scope.deferinside an immediately-invoked closure inside the loop (which scopes the defer to the closure, not the function).
How to fix it
Option 1 — wrap the loop body in a closure (or named function)
so defer fires per iteration:
for _, path := range paths {
if err := func() error {
f, err := os.Open(path)
if err != nil { return err }
defer f.Close()
return process(f)
}(); err != nil {
return err
}
}
Option 2 — close explicitly inside the loop:
for _, path := range paths {
f, err := os.Open(path)
if err != nil { return err }
err = process(f)
f.Close()
if err != nil { return err }
}
The closure form is more idiomatic for clean-up that involves
multiple defers (close + unlock + commit).