Start learning free

LESSON 50 OF 671 · FUNCTIONS I

Defer Basics in Go

Lesson

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 defer line 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 := 1 and defer 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).


STEP 1 OF 7
    Ctrl + Enter to check