Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using "if let..." with many expressions

Tags:

swift

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?

like image 390
Martin Cowie Avatar asked Jun 09 '14 11:06

Martin Cowie


3 Answers

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")
}
like image 178
Jean-Philippe Pellet Avatar answered Nov 11 '22 04:11

Jean-Philippe Pellet


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.)

like image 4
rickster Avatar answered Nov 11 '22 06:11

rickster


In Swift1.2,you could use multiple optional bindings.

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

Notice that y is implicitly declared as constant,which is the same as x.The above code is equivalent to the following.

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

If you want to explicitly declare y as mutable type,just type var before the variable name.

if let x = someDict[someKey],var y = someDict[someOtherKey] { ... }
like image 1
tounaobun Avatar answered Nov 11 '22 05:11

tounaobun