Stacks Image 11

Guard & Early exit in Swift

What is an early exit?

An early exit is terminating the execution of a function/method before it's last line of code has been reached.

There are two main cases where we had like to use an early exit:

  • we are in a particular case or the value can be determined faster, ie value in a cache,
  • all requirements are not met to finish executing the function, ie: missing parameters.

As for the reason we had like to use one is for readability as it allows to reduce imbrication.

Examples

Value in a cache

  • Without using an early exit:
var myValueOpt = myCache[key]

if myValueOpt == nil {
    // where the work actually takes place
    let computedValue = ...
    myValue = computedValue
    myCache[key] = computedValue
}
return myValueOpt!
  • Using an early exit:
if let myValue = myCache[key] {
    return myValue
}

// where the work actually takes place
let computedValue = ...
myCache[key] = computedValue

return computedValue

Those two versions do the same thing, use approximately the same number of lines, but the second one is easier to read as the main part of the code is located where it is meant to be: without being imbricated.

Requirement check

  • Without using an early exit:
if myRequirementCheck {
    // do something
}
  • Using an inverted logic early exit:
if !myRequirementCheck {
    return
}

// do something
  • Using a guard
guard myRequirementCheck else {
    return
}

// do something

Like with the cache example, using an early exit improve readability. The only real difference between inverted logic early exit and guard is how you sentence the condition check:

  • inverted logic early exit: if not success (failure) leave
  • guard: continue if success, else leave

How to choose between using an early exit check or a guard?

If you are looking for a case where you don't need to perform the full processing, then use an early exit.
If you are ensuring you can perform a processing, then the guard is for you.

Multiple checks in a guard

You are not limited to one check inside a guard, you can have many. You can even have a check requiring the previous one.

Instead of:

guard let object = anOptional else {
    return
}

guard let value = object.property else {
    return
}

You can write:

guard let object = anOptional,
    let value = object.property else {
    return
}

In a general case I think you should use the second version, which is shorter, as long as you don't need to distinguish the exact reason of the condition failure, ie: no need to return a different value or throw a different exception.