Skip to content
Theory · Optionals · lesson 4 of 8

guard let

Imagine you are getting ready to visit friends, and the first thing you do is look into the gift box. If it is empty, there is no point in going, and you call off the visit right away. If the gift is there, you take it with you and do not think about the check again.

A name from if let lives only inside the braces, so all the code that needs the value has to go inside them. With every new check, the code moves further to the right. A function is better off with a different order: check everything at the very start and leave right away if something is missing. Swift has guard for this:

SWIFT
func greeting(_ text: String) -> String {
    guard let age = Int(text) else {
        return "not a number"
    }
    guard age >= 0 else {
        return "negative age"
    }
    return "next year you turn \(age + 1)"
}
print(greeting("30"))  // next year you turn 31
print(greeting("abc")) // not a number
print(greeting("-5"))  // negative age

Let's go through the three calls:

  1. greeting("30"): the text holds a number, the first guard lets it through, and the constant age is 30. The second guard lets it through too: 30 is not below zero. The function returns next year you turn 31.
  2. greeting("abc"): there is no number, Int(text) gives nil, and the else of the first guard runs. return ends the function at once, and the remaining lines are never reached.
  3. greeting("-5"): there is a number, but the second guard sees that it is below zero, and its else returns negative age.

How the syntax works

The line guard let age = Int(text) else { … } has three parts:

  • guard—"make sure that…";
  • let age = Int(text)—the same optional binding as in if let;
  • else { … }—a block that runs only when the box holds nil.

This technique is called an early exit: the function stops as soon as it sees it cannot go on. All the checks stand at the start, and the main code follows below, at the left edge, without extra braces.

What guard gives you

The name lives after guard

With if let, the name exists inside the braces; with guard let, after the whole statement, to the end of the block that holds the guard. Most often that is the whole function. In the example, the constant age is declared in the first guard and used both in the second guard and in the last return.

else must leave

The only way past guard is with a value, so else must leave and not let execution continue. Of the statements you know, these do it:

  • in a function—return;
  • in a loop—continue to move on to the next iteration, or break to end the loop.

Here the gift picture is off. In real life, seeing an empty box, you can shrug and go visiting anyway. guard does not allow that: if else does not leave, the program does not compile.

guard with a plain condition

guard can be followed not only by let but by any condition of type Bool, like the second guard in the example. If the condition is false, else runs. In a loop, this is how unwanted values are skipped:

SWIFT
for n in 1...6 {
    guard n % 3 != 0 else {
        continue
    }
    print(n, terminator: " ") // 1 2 4 5
}

The guard reads as "n must not be divisible by 3". For 3 and 6 the condition is false, and continue moves on to the next number before reaching print.

guard let or if let

Interviewers like to ask this: "How does guard let differ from if let?" The differences fit in a table:

if letguard let
Where the name livesinside the bracesafter guard, to the end of the block
The else branchoptional, may do anythingrequired, and must leave
When it fitsthe value is needed in one placea check at the start of a function

Choose guard when there is no point in going on without the value, and if let when the program has a sensible path for nil too.

Common mistake

The else prints a message but does not leave:

SWIFT
func showAge(_ text: String) {
    guard let age = Int(text) else {
        print("not a number")
    } // ✗
    print(age)
}
TEXT
error: 'guard' body must not fall through, consider using a 'return' or 'throw' to exit the scope

The program does not compile: after print, execution would go on below guard, where age has no value. The compiler's hint names a way out you already know, return. Add it after print("not a number"). In a function without ->, return takes no value: it only ends the function.

Key points

  • guard let opens an optional, and the name from it lives after guard to the end of the block—most often the function.
  • The else branch of guard is required and must leave: for example, with return in a function, with continue or break in a loop.
  • guard also checks a plain condition: when it is false, else runs.
  • guard fits checks at the start of a function: the main code follows below without nested braces.