Guards and Nils - A Love Story
December 17, 2019
What is a guard?
A guard is like an if statement with one additional requirement: if the condition fails, then you must return.
That’s it. It’s a way to easily check an arbitrary number of integrity preconditions without using deeply nested if/else blocks that lead to ugly indentation.
Guards turn labyrinthine, 2-dimensional and heavily-indented code:
if error != nil {
return
} else {
if object == nil {
return
} else {
if object.id == "" {
return
} else {
print("OBJECT ID IS: \(object.id)")
}
}
}
into concise, 1-dimensional and sequential code:
guard error == nil else {
return
}
// ERROR-FREE ZONE!
guard object != nil else {
return
}
// OBJECT EXISTS ZONE!
guard object.id != "" else {
return
}
print("OBJECT ID IS: \(object.id)")
Isn’t it just cleaner? No indentation. A magical land where the linear sequence of code rather than nesting communicates the validity of integrtiy preconditions.
Why Guards Pair Well With Null-Safe Languages
Null-safe languages like Swift and Kotlin force you to answer the question “What happens if this object is nil?” at compiletime, rather than runtime.
So instead of receiving a surprising undefined
or NullPointerException
at runtime, or worse, in production, you receive compiler errors requesting that you as a responsible developer consider the null case.
Swift uses the question mark charcater ?
to indicate that an object is either the object, or nil. We call this an Optional type.
Here’s an Optional string:
// optionalString is either a String or nil
var optionalString: String?
The ?
indicates that optionalString
is either a String
or nil
.
Under the hood, an Optional is just an enum with two types: .some(Wrapped)
and .none
. If it is .some
, then the enum has an associated value Wrapped
which is the object itself. Here it is in just four lines of code:
public enum Optional<Wrapped>: ExpressibleByNilLiteral {
case none
case some(Wrapped)
}
Swift provides a number of ways to unwrap Optionals. My favorite is called optional binding:
guard let person = optionalPerson else {
return print("Person is nil! Must return!")
}
print(person.name)
Optional binding elegantly combines three operations:
-
- Initializes a new variable called
person
- Initializes a new variable called
-
- Assigns the value of
optionalPerson
toperson
- Assigns the value of
-
- Forces a return if the
optionalPerson
isnil
- Forces a return if the
Because we are compiletime required to return if the assignment of optionalPerson
to the new variable person
is nil, all code appearing below a guard can safely execute knowing that person
will not be nil.
All that without a single if/else indentation!
Thank you Chris Lattner and anyone who’s ever worked on one of the almost 100,000 (Jan 2020) commits to Swift :-)