Using "if let..." with many expressions

This idiom of Swift makes good sense

if let x = someDict[someKey] { ... }

However, what I really want is

if let x = someDict[someKey], y = someDict[someOtherKey] { ... }

As written this is not incorrect, but is this idea possible?


Solution 1:

Update for Swift 1.2

Since Swift 1.2, if let allows unwrapping multiple optionals, so you can now just write this, as in your example:

if let x = someDict[someKey], y = someDict[someOtherKey] { … }

You can even interleave conditions such as:

if let x = someDict[someKey] where x == "value", y = someDict[someOtherKey] { … }

This used to be valid before Swift 1.2

Here's how you would do it without an ugly force-upwrapping:

switch (dict["a"], dict["b"]) {
case let (.Some(a), .Some(b)):
    println("match")
default:
    println("no match")
}

Still pretty verbose, actually.

This works because an optional type of the form Type? is actually shorthand for Optional<Type>, which is an enum that looks roughly like this:

enum Optional<T> {
    case None
    case Some(T)
}

You can then use pattern matching as for any other enum.

Edit: I've seen people write helper functions like this one (sorry for the lack of attribution, I don't remember where I saw it):

func unwrap<A, B>(a: A?, b: B?) -> (A, B)? {
    switch (a, b) {
    case let (.Some(a), .Some(b)):
        return (a, b)
    default:
        return nil
    }
}

Then you can keep using the if let construct, namely like this:

if let (a, b) = unwrap(dict["a"], dict["b"]) {
    println("match: \(a), \(b)")
} else {
    println("no match")
}

Solution 2:

In Swift 1.2 (part of Xcode 6.3), the if let optional binding construct can bind multiple optionals, using the same syntax found in this question.

if let x = someDict[someKey], y = someDict[someOtherKey] { ... }

You can also extend this with conditions on the bound values:

if let a = foo(), b = bar(), a < b, let c = baz() { ... }

The switch pattern for handling multiple-optional binding (as seen in this answer) remains valid. Although with if let guard conditions you might find fewer use cases for it, it's still there in case you want to, say, handle cases of multiple optional binding with different behavior when different subsets of the optionals you're testing are nil.

(Note: Prior to Swift 3, if let a = ..., b = ..., a < b used where to split the binding from the conditional.)