Go has a keyword for "do this later, no matter what": defer. Put defer in front of a function call and the call does not run now — it is scheduled to run when the surrounding function returns. This is how real Go code guarantees cleanup happens, even when the function exits early:
func readConfig() {
f, _ := os.Open("config.txt")
defer f.Close() // scheduled NOW, runs when readConfig returns
// ... use f freely; it is guaranteed to be closed at the end
}
Three rules govern defer, and all three surprise beginners:
- Rule 1 — runs at function end. The deferred call fires when the function returns, not where the
deferline sits. - Rule 2 — LIFO order. Several defers stack up like plates: the last one deferred runs first.
- Rule 3 — arguments are evaluated at defer time. The call is delayed, but its arguments are computed immediately and frozen — a snapshot.
The trap:
First, defer must be given a real function call, parentheses and all. Forget them and the compiler stops you with exactly this error:
./prog.go:6:11: expression in defer must be function call
The subtler trap is Rule 3. Beginners expect a deferred print to see the variable's final value. It doesn't — the argument was snapshotted the moment the defer line ran:
n := 1
defer fmt.Println("n is", n) // n is evaluated NOW: snapshot = 1
n = 99
// function returns here... and prints: n is 1 (not 99!)
Real bugs come from this: logging a "final" status that is actually stale, or closing the wrong file in a loop. Learn to predict the snapshot and defer becomes your most reliable cleanup tool.
Assignment
Follow the Coding Tutorial to schedule three deferred prints around a mutating variable, so the printed order proves all three rules at once:
- Declare
count := 1anddefer fmt.Println("deferred: count was", count). - Set
count = 2, then defer the same print again. - Set
count = 3, then defer the same print a third time. - Finally print
fmt.Println("lesson body done, count =", count)— an ordinary, immediate call.
The exact expected output is:
lesson body done, count = 3
deferred: count was 3
deferred: count was 2
deferred: count was 1
Hint
Read the output against the rules: the body line prints first (Rule 1 — defers wait for the end), the deferred lines come out 3, 2, 1 (Rule 2 — LIFO), and each shows the value count had at its own defer line, not the final 3 (Rule 3 — snapshot).
Free plan: 30 tutor messages. PRO is unlimited.
