🗓️ 02112024 2256
📎
Core Concept: Defer executes a function when the surrounding function returns, regardless of how it returns.
Why It Matters
Guarantees cleanup (files, connections, locks) without try-finally blocks. Prevents resource leaks.
When to Use
✅ Use for:
- Cleanup of resources
 - Unlocking mutexes
 - Closing files/connections
 
❌ Don't use for:
- Long-running operations
 - Loops (defers accumulate)
 
vs Java try-with-resources
Java: try-with-resources auto-closes
Go: defer + Close() - explicit but guaranteed
WARNING
Defer in loop accumulates until function ends. Wrap in func() for per-iteration cleanup. ```
Trade-offs
Pros: Guaranteed cleanup, clear intent
Cons: Delayed execution, loop gotcha
Defer is commonly used with go_error_handling for cleanup before returns.
Quick Reference
// Basic usage
file, err := os.Open("data.txt")
if err != nil {
    return err
}
defer file.Close()  // Executes when function returns
// Multiple defers (LIFO order)
defer fmt.Println("First")
defer fmt.Println("Second")
defer fmt.Println("Third")
// Prints: Third, Second, First
// Loop pattern
for _, f := range files {
    func() {
        f, _ := os.Open(f)
        defer f.Close()  // Closes each iteration
    }()
}
| Pattern | Use Case | 
|---|---|
defer file.Close() | Close files | 
defer conn.Close() | Close connections | 
defer mu.Unlock() | Release locks | 
defer resp.Body.Close() | HTTP responses | 
Examples
EXAMPLE
Database transaction with rollback:
func TransferMoney(from, to int, amount float64) error {
    tx, err := db.Begin()
    if err != nil {
        return err
    }
    defer tx.Rollback()  // Safe to call even after Commit
    
    if err := debit(tx, from, amount); err != nil {
        return err  // Rollback happens automatically
    }
    
    if err := credit(tx, to, amount); err != nil {
        return err  // Rollback happens automatically
    }
    
    return tx.Commit()  // On success, commit wins
}
Mutex unlock pattern:
type SafeCounter struct {
    mu    sync.Mutex
    count int
}
func (c *SafeCounter) Increment() {
    c.mu.Lock()
    defer c.mu.Unlock()  // Guaranteed unlock even if panic
    
    c.count++
    // Complex logic here
    // Unlock happens regardless of return path
}
HTTP response cleanup:
func FetchData(url string) ([]byte, error) {
    resp, err := http.Get(url)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()  // Always close body
    
    return io.ReadAll(resp.Body)
}
```