What do nested optionals and nil assignments do: Int??, dictionaries of optionals, optional chaining and Int(String)?
Predict first
What does this print?
let a: Int?? = .some(nil)
let b: Int?? = nil
print(a == nil, b == nil, a as Any, b as Any)
var d: [String: Int?] = ["x": 1]
d["x"] = nil
print(d.count)
d["y"] = .some(nil)
print(d.count, d["y"] as Any, d["z"] as Any)
d.updateValue(nil, forKey: "w")
print(d.keys.sorted())
let maybe: [Int?] = [nil, 1, nil, 2]
print(maybe.compactMap { $0 }, maybe.count, maybe.map { $0 ?? -1 })
print(Int("12") as Any, Int("1.5") as Any, Int(" 3") as Any, Int("+4") as Any)
let words = ["1", "x", "3"]
print(words.map { Int($0) }, words.compactMap { Int($0) })
var o: Int? = 5
o = nil
o? += 1
print(o as Any)
let arr: [Int]? = []
print(arr?.first as Any, arr?.isEmpty as Any, arr?.count ?? 0)
- false true Optional(nil) nil / 0 / 1 Optional(nil) nil / ["w", "y"] / [1, 2] 4 [-1, 1, -1, 2] / Optional(12) nil nil Optional(4) / [Optional(1), nil, Optional(3)] [1, 3] / nil / nil Optional(true) 0
- true true nil nil / 1 / 1 nil nil / ["y"] / [1, 2] 4 [-1, 1, -1, 2] / Optional(12) Optional(1) Optional(3) Optional(4) / [1, nil, 3] [1, 3] / Optional(1) / nil true 0
- false false Optional(nil) nil / 0 / 0 nil nil / ["w"] / [1, 2] 2 [1, 2] / Optional(12) nil nil Optional(4) / [1, 3] [1, 3] / nil / nil Optional(true) 0
Mental model. A box inside a box: an empty outer box is different from an outer box that holds an empty inner box.
Int?? is an optional wrapping an optional. .some(nil) is not nil (a == nil is false), while a plain nil is. In a [String: Int?] the subscript returns Int??: d["x"] = nil removes the key (count 0), d["y"] = .some(nil) or updateValue(nil, forKey:) stores a nil value, and reading a missing key gives the outer nil while reading a stored nil gives Optional(nil).
Optional chaining adds one optional layer to the result: arr?.isEmpty is Optional(true), arr?.first is nil for an empty array, and o? += 1 on nil silently does nothing. Int("1.5") and Int(" 3") are nil (no trimming, no truncating) while Int("+4") is 4. map { Int($0) } keeps the nils; compactMap drops them.
Nil assignment removes, .some(nil) stores, chaining wraps once more.
Common traps
- Using
= nilto store a nil in a dictionary of optionals. It removes the key. - Expecting
Int(" 3")to trim. It is nil.
They might follow up with
- How do you flatten Int??
flatMap { $0 }ortry?-style flattening (Q15).
Related questions
- 1.2.1 What is an Optional in Swift, and what is it internally?
- 1.2.6 How does optional chaining (a?.b?.c) actually work?
- 1.2.7 What is flatMap vs map on Optional? What about Optional.map vs array map?
- 1.2.2 Why can switch on an Optional use .none / .some and also nil?
← Why do count, unicodeScalars, utf16 and utf8 give different numbers… In what order does switch try its cases, and what do where… →