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:
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 ageLet's go through the three calls:
greeting("30"): the text holds a number, the firstguardlets it through, and the constantageis30. The secondguardlets it through too:30is not below zero. The function returnsnext year you turn 31.greeting("abc"): there is no number,Int(text)givesnil, and theelseof the firstguardruns.returnends the function at once, and the remaining lines are never reached.greeting("-5"): there is a number, but the secondguardsees that it is below zero, and itselsereturnsnegative 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 inif let;else { … }—a block that runs only when the box holdsnil.
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—
continueto move on to the next iteration, orbreakto 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:
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 let | guard let | |
|---|---|---|
| Where the name lives | inside the braces | after guard, to the end of the block |
The else branch | optional, may do anything | required, and must leave |
| When it fits | the value is needed in one place | a 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:
func showAge(_ text: String) {
guard let age = Int(text) else {
print("not a number")
} // ✗
print(age)
}error: 'guard' body must not fall through, consider using a 'return' or 'throw' to exit the scopeThe 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 letopens an optional, and the name from it lives afterguardto the end of the block—most often the function.- The
elsebranch ofguardis required and must leave: for example, withreturnin a function, withcontinueorbreakin a loop. guardalso checks a plain condition: when it is false,elseruns.guardfits checks at the start of a function: the main code follows below without nested braces.