Skip to content
Theory · Enums · lesson 7 of 8

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:

SWIFT
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:

  1. Int("42") gives Int?, which is Optional<Int>. The string "42" holds a number, so parsed holds .some(42).
  2. The pattern .some(let n) fits a box with a gift and gets 42 out into the constant n, the way .coffee(let size) got the size off the ticket.
  3. The branch case .none: is there for the empty box. It does not run here, but without it the switch would not be exhaustive.
  4. 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:

  • .some is a box with a gift; the value itself sits in this case as an associated value;
  • .none is an empty box; this is exactly nil.

You can also write the cases explicitly:

SWIFT
let empty: Int? = .none
let five: Int? = .some(5)
let sameFive: Int? = 5
print(empty == nil) // true
print(five == sameFive) // true

Let's walk through the program:

  1. empty holds .none, and the comparison with nil prints true: nil is a short way to write .none.
  2. .some(5) explicitly puts the number 5 into the box five.
  3. In sameFive the number is written without .some. Then Swift wraps it in .some by itself.
  4. That is why five == sameFive prints true: 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:

SWIFT
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 parsed holds the case .some, and get its value out into n;
  • if case works with any enum, while if let works only with an optional;
  • you can add else to 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:

SWIFT
switch parsed { // ✗
case .some(let n):
    print("number", n)
}
TEXT
error: switch must be exhaustive

The 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

  • Optional is an enum with the cases .some, holding the value, and .none; nil is .none.
  • Swift itself wraps a value you put into an optional in .some.
  • A switch on an optional handles both cases: .some(let n) gets the value out, and .none catches the empty box.
  • if let n = parsed is a short form of if case .some(let n) = parsed.