What Defer is
Defer is a powerful feature in Go that allows you to schedule a function call to be executed just before the surrounding function returns. This is particularly useful for resource management, such as closing files, releasing locks, or cleaning up resources.
func readFile() {
file, _ := os.Open("example.txt")
defer file.Close()
...
Even if readFile hits return early or panics, file.Close() will still be called.
Basic Behavior
- Order: Deferred functions are executed in LIFO (Last In, First Out) order.
func main() {
defer fmt.Println("First")
defer fmt.Println("Second")
defer fmt.Println("Third")
fmt.Println("Function Body")
}
// Output:
Function Body
Third
Second
First
- Arguments: Arguments to deferred functions are evaluated immediately when the defer statement is executed, not when the function is called.
func main() {
x := 10
defer fmt.Println(x) // x is evaluated here, so it prints 10
x = 20
// Output:
10
}
but if you write a code like below, the closure captures x by reference, which means deferred function will see the current value of x at execution time, not at defer-time
func main() {
x := 10
defer func() { fmt.Println(x) }()
x = 20
// Output:
20
}
Under the Hood
when you write code like defer f(a,b), Go compiler does two main things.
- Evaluates arguments immediately
- a and b are evaluated at that point
- Stored in the
_defernode
- Calls a runtime helper(
runtime,deferproc)- Allocates a
_deferstruct - Links it to the current goroutine’s defer list
- Stores:
- Function pointer
- Arguments
- Frame pointer(
sp) of the current function - Link to next dfer node
- Allocates a
So the struct looks like below:
+-------------------------------+
| _defer node |
+-------------------------------+
| fn | pointer to function | <- deferred function to call
+--------+----------------------+
| frame | pointer to stack frame| <- which function frame owns this defer
+--------+----------------------+
| args | evaluated arguments | <- values captured at defer creation
+--------+----------------------+
| link | *_defer | <- next node in goroutine’s defer list
+-------------------------------+
| other | runtime bookkeeping | <- panic state, active flags, etc.
+-------------------------------+
When the surrounding function returns, Go runtime:
- Calls
runtime.deferreturn(generated by the compiler for each function exit) - Walks the linked list of defers
- Executes only the nodes that belong to the current function frame (Uses
framepointer to check) - Pops the executed nodes from the list
Please note, In the Go runtime, there is actually just one linked list per goroutine, not
one per function .
But each _defer node knows which stack frame it belongs to. That’s how the runtime knows which defers to
execute when a function returns.
Usage
- Resource cleanup: Please use
deferimmediately after acquiring a resource like files, network connections, timers to ensure it gets released properly. - Mutexes and locks:
mu.Lock() defer mu.Unlock() - Handling panics with recover:
defer func() { if r := recover(); r != nil { fmt.Println("Recovered from panic:", r) } }()