Optional as an enum
An optional looks like a special type. It has its own ? sign, its own word nil and its own ways to get the value out: if let and ??. But behind all of this stands an ordinary enum with two cases.
Remember the gift box from the optionals theme. It holds either a gift or nothing, and there is no third option. This is a menu with two lines: "a box with a gift" and "an empty box". The gift itself is like the coffee size on a ticket: a detail, and each box has its own.
Since an optional is an enum, you can handle its cases in a switch:
let parsed = Int("42")
switch parsed {
case .some(let n):
print("number", n) // number 42
case .none:
print("no number")
}Let's go through the program line by line:
Int("42")givesInt?, which isOptional<Int>. The string"42"holds a number, soparsedholds.some(42).- The pattern
.some(let n)fits a box with a gift and gets42out into the constantn, the way.coffee(let size)got the size off the ticket. - The branch
case .none:is there for the empty box. It does not run here, but without it theswitchwould not be exhaustive. - The program prints
number 42.
The cases .some and .none
Interviewers like to ask this: how is Optional built? The answer: it is an enum with two cases:
.someis a box with a gift; the value itself sits in this case as an associated value;.noneis an empty box; this is exactlynil.
You can also write the cases explicitly:
let empty: Int? = .none
let five: Int? = .some(5)
let sameFive: Int? = 5
print(empty == nil) // true
print(five == sameFive) // trueLet's walk through the program:
emptyholds.none, and the comparison withnilprintstrue:nilis a short way to write.none..some(5)explicitly puts the number5into the boxfive.- In
sameFivethe number is written without.some. Then Swift wraps it in.someby itself. - That is why
five == sameFiveprintstrue: both boxes hold.some(5).
In everyday code, .some and .none are rarely written: 5 and nil are shorter and mean the same.
Here the menu picture is inaccurate. A menu is written for one café, but the enum Optional is one for all types. In Int? the gift is a number, in String? a string, and Dish(rawValue: 2) gives Dish?, a box with a dish.
if let and if case
You already know if let. It is a short form of the same check for a case. Compare it with if case:
if case .some(let n) = parsed {
print("number", n) // number 42
}
if let n = parsed {
print("number", n) // number 42
}What to know about these two forms:
- both check whether
parsedholds the case.some, and get its value out inton; if caseworks with any enum, whileif letworks only with an optional;- you can add
elseto either: that branch runs for.none.
?? is built the same way: for .some it gives the value from the box, and for .none the default value.
Common mistake
A switch on an optional handled only the box with a gift:
switch parsed { // ✗
case .some(let n):
print("number", n)
}error: switch must be exhaustiveThe program does not compile. Optional is an enum, and a switch on it must handle both cases, like a switch on any other enum. Below it, the compiler suggests add missing case: '.none', which means "add a branch for .none". Write the branch case .none: for the empty box.
Key points
Optionalis an enum with the cases.some, holding the value, and.none;nilis.none.- Swift itself wraps a value you put into an optional in
.some. - A
switchon an optional handles both cases:.some(let n)gets the value out, and.nonecatches the empty box. if let n = parsedis a short form ofif case .some(let n) = parsed.