GingerBreadMane
GingerBreadMane

Reputation: 2697

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?

Upvotes: 3

Views: 2325

Answers (3)

Rob
Rob

Reputation: 437442

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.

Upvotes: 3

Nebojsa Nadj
Nebojsa Nadj

Reputation: 631

There are two ways I would go around doing this.

  1. guard let someString = someString else { return }

This just checks the value, with the same variable name.

  1. guard someString != nil else { return }

Doing this second one, you can even declare it with a bang(!) it will be "pre-defined" to a "non-optional" in a way.

So now you could declare it like:

var someString:String! = "hello"

I think you are looking for exactly this.

EDIT: And now you can use your own example.

guard someString != nil, nonOptional == someString else { return }

Upvotes: 1

matt
matt

Reputation: 534958

If the point is to make the assignment only if the assignment is possible, write:

nonOptionalString = someString ?? nonOptionalString

If you really also need the return if the assignment is impossible, then just write an if/else clause:

if someString != nil {
    nonOptionalString = someString!
} else {
    return
}

In other words, stop trying to be so fancy and just say what you mean.

Upvotes: 0

Related Questions