Defer
Defer is a keyword in Go that schedules a function call to be executed after the surrounding function returns. It's commonly used for cleanup tasks like closing files, unlocking mutexes, or releasing resources.
Key Points
- Deferred functions execute in LIFO (Last In, First Out) order
- Arguments to deferred functions are evaluated immediately
- Deferred functions run even if a panic occurs
- Defer is commonly used for resource cleanup
- Multiple defers stack up - last deferred runs first
- Deferred functions can modify named return values
- Defer has a small performance cost - use judiciously in hot paths
Basic Defer Usage
Defer schedules a function call to run after the current function completes:
package main
import "fmt"
func main() {
defer fmt.Println("world")
fmt.Println("hello")
}hello
worldDefer Execution Order
Multiple deferred calls are executed in reverse order (LIFO):
package main
import "fmt"
func main() {
fmt.Println("counting")
for i := 0; i < 3; i++ {
defer fmt.Println(i)
}
fmt.Println("done")
}counting
done
2
1
0Defer with File Handling
One of the most common uses of defer is closing files and resources:
package main
import (
"fmt"
"os"
)
func main() {
f, err := os.Create("test.txt")
if err != nil {
fmt.Println("error:", err)
return
}
defer f.Close()
// Write to file
f.WriteString("Hello, defer!")
fmt.Println("file written successfully")
// File automatically closed when function returns
}file written successfullyDefer Behavior
| Aspect | Behavior | Example |
|---|---|---|
| Execution Order | LIFO (Last In, First Out) | Last defer executes first |
| Argument Evaluation | Arguments evaluated immediately | defer fmt.Println(i) captures current i |
| Execution Timing | After return statement, before return to caller | Can modify named return values |
| Panic Handling | Deferred functions still run during panic | Used for cleanup in error scenarios |
| Return Values | Can modify named return values | Useful for error handling patterns |
| Scope | Function-level, not block-level | Defers in loops accumulate |
Common Defer Patterns
| Pattern | Code | Use Case |
|---|---|---|
| File Closing | defer file.Close() | Ensure files are closed |
| Mutex Unlocking | mu.Lock(); defer mu.Unlock() | Ensure locks are released |
| Connection Closing | defer conn.Close() | Close database/network connections |
| Timing Functions | defer timeTrack(time.Now()) | Measure function execution time |
| Cleanup Resources | defer cleanup() | General resource cleanup |
| Recover from Panic | defer func() { recover() }() | Catch and handle panics |
Defer Gotchas
| Issue | Problem | Solution |
|---|---|---|
| Loop Variables | for _, f := range files { defer f.Close() } | Defers accumulate, close immediately or use closure |
| Argument Evaluation | defer fmt.Println(x) captures x now | Use closure if you need final value: defer func() { fmt.Println(x) }() |
| Return Value Shadowing | Unnamed returns not modifiable | Use named return values if defer needs to modify |
| Performance | Small overhead per defer | Avoid in tight loops or hot paths |
| Error Handling | defer f.Close() ignores errors | Check errors: defer func() { err = f.Close() }() |
Advanced Defer Examples
| Technique | Example | Description |
|---|---|---|
| Modify Return Value | func() (err error) { defer func() { if err != nil { err = wrap(err) } }(); ... } | Wrap errors on return |
| Function Timing | defer func(t time.Time) { log.Println(time.Since(t)) }(time.Now()) | Log execution duration |
| Transaction Rollback | defer func() { if err != nil { tx.Rollback() } else { tx.Commit() } }() | Conditional cleanup based on error |
| Panic Recovery | defer func() { if r := recover(); r != nil { log.Println(r) } }() | Gracefully handle panics |
| Stack Tracing | defer log.Println("exiting function") | Track function entry/exit |
| Cleanup Chain | defer cleanup1(); defer cleanup2(); defer cleanup3() | Multiple cleanup steps in order |