Skip to content
Theory · Errors · lesson 6 of 7

defer

In the evening, a post office clerk closes their counter however the shift went: they sorted all the parcels, or they sent the last one back with a stamp. So as not to forget, in the morning, right after opening the counter, they hang a note on it: "close when leaving". The note is read on the way out, whichever way the clerk leaves.

A function can also have several exits:

  • return at the end;
  • return in a guard branch;
  • throw.

If the same thing must be done before each exit, sooner or later one of the exits gets forgotten. For this, Swift has defer:

SWIFT
enum ParseError: Error {
    case badFormat
}

func parse(_ text: String) throws -> Int {
    print("open", terminator: " ")
    defer { print("close", terminator: " ") }
    guard let n = Int(text) else {
        throw ParseError.badFormat
    }
    return n
}

print((try? parse("3")) ?? 0) // open close 3
print((try? parse("x")) ?? 0) // open close 0

Let's go through the program step by step:

  1. print("open", terminator: " ") prints open: the counter has opened.
  2. defer { … } hangs the note. The code in the braces does not run now: it is put off until the function exits.
  3. In the call parse("3"), the function exits through return n. Right before the exit, defer fires and prints close. Only then does the outer print print the result 3.
  4. In the call parse("x"), the function exits through throw, and defer fires all the same. try? turns the error into nil, and ?? 0 gives 0.

When defer fires

The code in defer runs on leaving the block in braces where the defer is written. It does not matter how the block is left:

  • through return;
  • through throw;
  • when the block's lines run out.

A block is not only a function body. A defer in a loop body fires at the end of every lap, and a defer in a do block fires on leaving do.

Several defer blocks

One block can hold several defer blocks. Then they fire in reverse order: the last one written runs first.

SWIFT
func shift() {
    print("unlock", terminator: " ")
    defer { print("lock") }
    print("lamp on", terminator: " ")
    defer { print("lamp off", terminator: " ") }
    print("work", terminator: " ")
}

shift() // unlock lamp on work lamp off lock

Let's walk through the function:

  1. The clerk unlocks the door and hangs the note "lock up".
  2. They turn on the lamp and hang a second note: "turn off the lamp".
  3. They work: work is printed.
  4. On the way out, the notes are read starting from the last one: first lamp off, then lock.

The reverse order is handy: what was opened last is closed first. The lamp goes off while the door is still unlocked.

Interviewers like to ask this: when does defer run, and in what order? The answer:

  • on leaving the block where it is written: through return, through throw or at the end of the block;
  • not at its own line, but after all the other lines of the block;
  • several defer blocks run in reverse order: the last one written runs first.

Here the note picture is off. A note on the counter is visible all day, and it can be carried out at any time. A defer cannot be carried out early: it fires only on leaving its block, and only if the program has reached its line.

Common mistake

defer was written below a line that can leave the function:

SWIFT
func late(_ text: String) throws -> Int {
    print("open", terminator: " ")
    guard let n = Int(text) else {
        throw ParseError.badFormat
    }
    defer { print("close", terminator: " ") } // ✗
    return n
}

The program compiles, but for text that is not all digits, close is not printed. The function leaves through throw before it reaches the line with defer, so the note never gets hung. Write defer right after the thing it must close, before the first line that can leave the function.

Key points

  • defer puts code off until the block where it is written is left: through return, through throw or at the end of the block.
  • The code in defer runs after all the other lines of the block, not at its own line.
  • Several defer blocks run in reverse order: the last one written runs first.
  • defer fires only if execution has reached its line, so it is written before the first possible exit.