go.defer_in_loop
Performance
Medium
Detects defer statements inside loops.
Why It Matters
Section titled “Why It Matters”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.
Example
Section titled “Example”// ❌ Beforefor _, 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()}What Unfault Detects
Section titled “What Unfault Detects”deferinsideforloopsdeferinsiderangeloops- Nested loop defer patterns
Auto-Fix
Section titled “Auto-Fix”Unfault wraps the loop body in an immediately-invoked function literal (IIFE).
Best Practices
Section titled “Best Practices”// Extract to helper functionfunc 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 }}