Two (or more) optionals in Swift
Asked Answered
S

2

16

While watching an Apple's video about LLDB debugger I found something I can't find an explanation for; he was talking about optional values when he wrote:

var optional: String? = nil; //This is ok, a common optional
var twice_optional: String?? = nil; //What is this and why's this useful??

I opened a playground and started trying it out and realized that you can write as many as ? as you want, and then unwrap them with the same number of !. I understand the concept of wrapping/unwrapping a variable but can't think of a situation where I would like to wrap a value 4, 5 or 6 times.

Sedition answered 1/12, 2014 at 9:26 Comment(0)
L
16

(Updated for Swift >=3)

"Double optionals" can be useful, and the Swift blog entry "Optionals Case Study: valuesForKeys" describes an application.

Here is a simplified example:

let dict : [String : String?] = ["a" : "foo" , "b" : nil]

is a dictionary with optional strings as values. Therefore

let val = dict[key]

has the type String?? aka Optional<Optional<String>>. It is .none (or nil) if the key is not present in the dictionary, and .some(x) otherwise. In the second case, x is a String? aka Optional<String> and can be .none (or nil) or .some(s) where s is a String.

You can use nested optional binding to check for the various cases:

for key in ["a", "b", "c"] {

    let val = dict[key]
    if let x = val {
        if let s = x {
            print("\(key): \(s)")
        } else {
            print("\(key): nil")
        }
    } else {
        print("\(key): not present")
    }

}

Output:

a: foo
b: nil
c: not present

It might be instructive to see how the same can be achieved with pattern matching in a switch-statement:

let val = dict[key]
switch val {
case .some(.some(let s)):
    print("\(key): \(s)")
case .some(.none):
    print("\(key): nil")
case .none:
    print("\(key): not present")
}

or, using the x? pattern as a synonym for .some(x):

let val = dict[key]
switch val {
case let (s??):
    print("\(key): \(s)")
case let (s?):
    print("\(key): nil")
case nil:
    print("\(key): not present")
}

(I do not know a sensible application for more deeply nested optionals.)

Ledbetter answered 1/12, 2014 at 10:41 Comment(3)
Also, to be exhaustive, a try? ... as? ... will return a double Optional.Thitherto
@Cœur: Yes, but that can be solved with parentheses: if let value = (try? ...) as? ... { ... }, because optional binding of an optional does not add another optional level.Ledbetter
Then, another example, UIApplication.shared.delegate?.window is a double optional. You'll need to add ?? to unwrap the window.Thitherto
T
3
var tripleOptional: String???

is same as

var tripleOptional: Optional<Optional<Optional<String>>>

I cannot think of any useful use of it, but its there because optionals are generic work for any type of object. Nested optionals are like box put into another box or array but into an other array.

Transistor answered 1/12, 2014 at 9:56 Comment(2)
Yes, you're right. But even arrays of arrays of arrays can be useful in some situations. Given that optionals are built-in the language i think they could've controlled how many times you can wrap a value in an optional type, even if Optional<T> accepts any type. Well let's see if someone opens up our eyesSedition
But why would you want to restrict it? If you restrict optional to four levels deep, 99.99% of developers would never notice, and 0.01% would be very, very angry.Americana

© 2022 - 2024 — McMap. All rights reserved.