Go Defer
The defer keyword schedules a function call to run just before the surrounding function returns. No matter how the function exits — through a normal return, an early return, or even a panic — deferred calls always run. This makes defer ideal for cleanup tasks like closing files, releasing locks, or logging exit points.
Basic Defer
package main
import "fmt"
func main() {
defer fmt.Println("World") // scheduled, runs last
fmt.Println("Hello")
}
Output:
Hello
World
Even though defer fmt.Println("World") appears first in the code, it runs after fmt.Println("Hello") because defer waits until the function is about to return.
Defer Execution Flow
func main() {
defer fmt.Println("3rd – deferred") ← scheduled 3rd, runs 3rd
defer fmt.Println("2nd – deferred") ← scheduled 2nd, runs 2nd
defer fmt.Println("1st – deferred") ← scheduled 1st, runs 1st
fmt.Println("Normal execution")
}
Execution order:
1. "Normal execution"
2. "1st – deferred" ← LIFO: last in, first out
3. "2nd – deferred"
4. "3rd – deferred"
Multiple deferred calls execute in LIFO order — Last In, First Out. The last defer scheduled runs first.
LIFO Stack Diagram
Defer stack (like a stack of plates):
defer C ← pushed last
defer B
defer A ← pushed first
When function returns:
C runs first (last in, first out)
B runs second
A runs last
Defer for Cleanup – Closing a File
The most common real-world use of defer is ensuring a resource is released after it is opened, regardless of what happens in between.
package main
import (
"fmt"
"os"
)
func readFile(name string) {
file, err := os.Open(name)
if err != nil {
fmt.Println("Error:", err)
return
}
defer file.Close() // always runs before readFile returns
fmt.Println("File opened:", name)
// ... read file contents ...
}
func main() {
readFile("notes.txt")
}
Placing defer file.Close() immediately after opening the file guarantees it closes, even if the function returns early or panics.
Defer Arguments Are Evaluated Immediately
The arguments passed to a deferred function are evaluated at the moment defer runs, not when the deferred call executes.
package main
import "fmt"
func main() {
x := 10
defer fmt.Println("Deferred x:", x) // x is captured as 10 here
x = 99
fmt.Println("Current x:", x)
}
Output:
Current x: 99
Deferred x: 10
The deferred call captured x = 10 when the defer line ran, even though x changed to 99 afterward.
Common Uses of Defer
| Use Case | Example |
|---|---|
| Close a file | defer file.Close() |
| Unlock a mutex | defer mu.Unlock() |
| Close a database connection | defer db.Close() |
| Log function exit | defer fmt.Println("function done") |
| Recover from panic | defer recover() |
Key Points
- Deferred calls run just before the surrounding function returns
- Multiple defers execute in LIFO order — last deferred runs first
- Arguments to deferred calls are evaluated when the defer line executes, not when the call runs
- Use defer for cleanup tasks to guarantee they always run, even on error paths
