Search code examples
swiftoption-typeunwrap

Unwrapping optionals to a pre-defined variable in the guard condition without creating a new constant


The Swift guard statement allows you to unwrap optionals to a new constant and do an early return if the assignment fails.

var someString:String? = "hello"
...
...  
guard let newString = someString 
   else {
     return
   }

...

If I want to unwrap an optional and set it to a pre-defined non-optional variable, I've been first unwrapping to the new constant (newString) and then setting the non-optional variable after the guard statement like this:

var someString:String? = "hello"
var nonOptionalString:String = "bonjour"
...
...
guard let newString = someString 
   else {
     return
   }
nonOptionalString = newString

...

Is there a way to set a pre-defined, non-optional var within the condition of the guard statement without creating a new constant? Something like the following (which doesn't work)?

var someString:String? = "hello"
var nonOptionalString:String = "bonjour"
...
...
guard nonOptionalString = someString 
   else {
     return
   }
...

If something like this isn't possible, is there an underlying philosophy behind the Swift language design or technical reason as to why this doesn't exist?


Solution

  • I would just test for nil and then force unwrap when I know it's not:

    var someString: String? = "hello"
    let nonOptionalString: String       // note, you don't have to initialize this with some bogus value
    
    guard someString != nil else { return }
    nonOptionalString = someString!
    

    Or if someString was a parameter to some method or closure, you can unwrap in the guard statement using the same variable name, simplifying life even more:

    func foo(someString: String?) {
        guard let someString = someString else { return }
    
        // now I can just use local `someString`, which is not optional anymore
    }
    

    If you're desperate to unwrap and exit-if-nil in a single statement, you could theoretically write a function to unwrap if it can or throw an error if it can't:

    extension Optional {
        enum OptionalError: Error {
            case unwrapFailed
        }
    
        func unwrap<T>() throws -> T {
            if self == nil { throw OptionalError.unwrapFailed }
            return self as! T
        }
    }
    

    Then you can do:

    do {
        firstNonOptional  = try firstOptional.unwrap()
        secondNonOptional = try secondOptional.unwrap()
        thirdNonOptional  = try thirdOptional.unwrap()
    } catch {
        return
    }
    

    I think that's horrible overkill, but if you're desperate to distill it down to one line per unwrap, that's one way to do it.