1. 13
  1.  

  2. 11

    I must be missing something. How is guard/else different than if-not?

    1. 12

      I realize that the lede is sort of buried in the description of guard. Let me try to elaborate.

      In short, guard doesn’t drastically increase the expressiveness of the language. Instead, it provides a way to ensure that an invariant is met before execution continues. It’s most useful to avoid deeply nesting code when you need to ensure a bunch of values aren’t nil one after the other. Let me explain further:

      If you have a regular boolean predicate, guard doesn’t really buy you much. The following two pieces of code are equivalent:

      func example1() {
          if somePredicate() {
              return
          }
          doSomething()
      }
      
      func example2() {
          guard somePredicate() else {
              return
          }
          doSomething()
      }
      

      The only benefit you get is that if you don’t return from the function in the guard’s else clause, the compiler will complain. But that in and of itself is hardly worth adding a new control flow statement.

      So what is the real benefit of guard? First, let’s talk about optionals. In Swift, variables are non-nullable by default. If I declare a variable of type String, I can’t set it to nil (i.e. null). This is unlike e.g. Java or Objective-C:

      // This will not compile!
      var myString : String = nil
      

      If you want your variable to be nullable, you have to declare it as an optional type. To avoid going into a lot of detail, the declaration would look like this:

      // This will compile. 'String?' = "optional containing String", or "Optional<String>".
      var myString : String? = nil
      

      Note that an Optional<String> is not the same as a String, sort of like how an array of strings (Array<String>) is not the same as a String. Most functions and methods don’t take optional arguments, nor are most variables of optional type. So how do you work with optionals in Swift? One language construct is something known as if-let, which lets you ‘unwrap’ an optional’s underlying value, if it has one:

      var maybeAString : String? = // ...
      
      if let definitelyAString = maybeAString {
          // definitelyAString is of type String
          doSomething(definitelyAString)
      }
      

      This basically means, “if maybeAString actually contains a String value, assign its value to definitelyAString, which is of type String, so you can do stuff with it. Otherwise, if maybeAString is nil, skip that code.”

      Now possible problems with if-let are:

      • You have another level of indentation. What if you need to ‘unwrap’ two more values? Now you have what looks like a pyramid of code.
      • If you have multiple levels of if-let unwrapping, the else clauses become hard to follow.

      Here is where guard comes in. guard lets you restructure your code to make it more clear what is happening when you unwrap optionals, because once you unwrap the optional, you can use it directly in the rest of the function. Here is an example, consisting of two functions that do the same thing:

      // First example: using 'guard'
      func example() -> Bool {
          var foo : String? = maybeGetAString()
      
          guard let actualFoo = foo else {
              println("foo was nil! exiting...")
              return false
          }
      
          // At this point, I can use 'actualFoo', of type 'String',
          // in the rest of my function.
      
          var bar : Int? = takeAStringAndMaybeReturnAnInt(actualFoo)
      
          guard let actualBar = bar else {
              println("bar was nil! exiting")
              return false
          }
      
          return turnAnIntIntoABoolean(actualBar)
      }
      
      // Same as first example, but using 'if-let' instead of 'guard'
      func example2() -> Bool {
          var foo : String? = maybeGetAString()
          if let actualFoo = foo {
              var bar : Int? = takeAStringAndMaybeReturnAnInt(actualFoo)
              if let actualBar = bar {
                  return turnAnIntIntoABoolean(actualBar)
              } else {
                  println("bar was nil! exiting")
                  return false
              }
          } else {
              println("foo was nil! exiting")
              return false
          }
      }
      

      (I think) the first example is easier to follow. It’s clear that you unwrap foo first, use foo’s value to get bar, and then unwrap bar, before using its value to get the final value. It’s also clearer what happens when execution doesn’t follow the happy path: we first check foo, and then do something if it’s nil. Then we check bar, and do something else if it’s nil. This is a pretty simple example, but you can see how the if-let variant might become hard to parse if you keep on nesting if-let conditionals.

      Let me know if that helps.

      1. 3

        This certainly clears it up! Thank you.

        I should add: the length of this explanation + the original blog post suggests one of two things: 1) This is needless complex (I don’t think it is) or 2) You guys need to tune your presentation of this information :-)

        1. 1

          I’m glad it helped! I think the description is long, unfortunately, because guard’s reason for existing only makes sense if you’re familiar with Swift’s optional system, which itself requires a bit of explanation. I will say I could have gotten to the point sooner, though.

        2. 1

          I just realized I made a mistake in the first example. Corrected:

          func example1() {
              if somePredicate() == false {
                  return
              }
              doSomething()
          }
          
          func example2() {
              guard somePredicate() else {
                  return
              }
              doSomething()
          }
          
      2. -2

        Looks like they have been busy adding non-essential language features which could have been implemented in a library…