What does defer guarantee? What is the order of multiple defers?

1.8.4 · L2 · Interview

Predict first

What does work() print?

func work() {
    defer { print("A") }
    defer { print("B") }
    print("body")
}

work()
  • body A B
  • body B A
  • A B body

Mental model. Defers are plates stacked on a spring. The last plate you put on is the first one that comes off.

defer runs when the current scope exits — by return, throw, break, or falling off the end. It runs even on the error path, which is why it is the right place for cleanup.

Multiple defers run LIFO: the last one registered runs first. A defer inside an inner block runs when that block exits, not at function end — the classic puzzle in Q27 of the bank.

Defers run when the scope exits, last registered first.

Code

func work() {
    defer { print("A") }
    defer { print("B") }
    print("body")
}

func nested() {
    defer { print("outer") }
    do {
        defer { print("inner") }
        print("in do")
    }
    print("after do")
}

work()
nested()
body, B, A, in do, inner, after do, outer

Common traps

  • “defer runs at function return only.” It is scope-based. A defer in an if or do block fires at the end of that block.
  • “defer cannot see the return value.” It cannot change the returned value after the return expression is evaluated, but it can read variables in scope.
  • Forgetting that defer runs during a throw. That is the point — cleanup happens on both paths.

They might follow up with

  • Can you return from inside a defer? No — that is a compile error.
  • Does a defer in a loop run every iteration? Yes, if the loop body creates a scope — which it does. Each iteration registers and fires its own defer.

Related questions

  • 1.8.2 try, try?, try! — and what is rethrows?
  • 1.4.7 What is generic specialization? When does it not happen?

← What are typed throws (throws(MyError))? Why do they exist?   What are consume, borrowing, and noncopyable types, at a high level? →