Skip to content

go.defer_in_loop

Performance Medium

Detects defer statements inside loops.

Deferred calls accumulate until the function returns, not until the loop iteration ends:

  • Resource exhaustion — File handles accumulate until function exits
  • Memory leak — Deferred closures hold references
  • Crash under load — Works fine with 10 items, crashes with 10,000
  • Counter-intuitive — Common misconception even among experienced Go devs

This is one of Go’s most common gotchas.

// ❌ Before
for _, file := range files {
f, err := os.Open(file)
if err != nil {
return err
}
defer f.Close() // Doesn't close until function returns!
// Process file...
}

If you have 10,000 files, you open 10,000 file handles before closing any.

// ✅ After (closure)
for _, file := range files {
func() {
f, err := os.Open(file)
if err != nil {
return
}
defer f.Close()
// Process file...
}()
}
// ✅ After (explicit close)
for _, file := range files {
f, err := os.Open(file)
if err != nil {
return err
}
// Process file...
f.Close()
}
  • defer inside for loops
  • defer inside range loops
  • Nested loop defer patterns

Unfault wraps the loop body in an immediately-invoked function literal (IIFE).

// Extract to helper function
func processFile(path string) error {
f, err := os.Open(path)
if err != nil {
return err
}
defer f.Close()
// Process...
return nil
}
for _, file := range files {
if err := processFile(file); err != nil {
return err
}
}