Dear Erica: Help me guard for nil variable

Hi Erica,

I was curious if you had a more elegant solution for this particular situation in Swift: Say you want to check that a specific variable is nil before continuing; it’s much more common to do the opposite. Right now, I have something like:

guard thing == nil else {
   if let thing = thing {
      doSomething(withThing: thing)
    }
   return
}

Is there a better way to do this? I could change to an `if` statement, but I like that the guard statement insures I have to exit. Logically, it would be safe to force-unwrap, but meh.

– Rob

Rob, yes, there’s a much easier approach. You’re using guard here as a kind of “if statement” and that’s backwards to the way it’s intended. Instead, use guard to unwrap the “thing” and it that fails exit scope. If a your guard statement’s “else” clause is significantly larger than a line or two, you’re probably doing things wrong.

Here’s the correct way to do it:

guard let thing = thing else { return } // leave scope if thing cannot be unwrapped
doSomething(with: thing) // thing is now unwrapped

This updated code follows a common Swift pattern: use a guard statement to shadow an optional value. Variable shadowing with guard enables you to use the same name (“thing”) in the current scope. Once you pass that guard statement, your item is unwrapped and can be used without further testing against nil.

You are right to avoid forced unwrapping. Challenge yourself to justify each and every exclamation point in your code.

4 Comments

  • That’s wrong. He wants to doSomething only in the failing case and continue doing some more stuff only if the value is null.

    • You have a point but this is sourced from an email conversation, and he confirmed “Ok, so you’re saying I should invert what I have so I can unwrap with the guard normally? Makes sense.”

  • In situations like this I just do

    if let thing = thing {
    doSomething(with: thing)
    return
    }

    and the rest of the body of the method continues as normal. Guard is great, but that doesn’t mean it has to be used in all instances.

  • you can use like this:
    guard (thing.flatMap{doSomething(thing: $0)}) != nil else { return }