In what order does switch try its cases, and what do where, fallthrough, for-case and ~= do?
Predict first
What does this print?
func describe(_ x: Int, _ y: Int) -> String {
switch (x, y) {
case (0, 0): return "origin"
case (let a, 0) where a > 0: return "pos x-axis \(a)"
case (_, 0): return "x-axis"
case let (a, b) where a == b: return "diagonal \(a)"
case (1...3, _): return "small x"
case (_, let b) where b.isMultiple(of: 2): return "even y"
default: return "other"
}
}
for p in [(0, 0), (5, 0), (-2, 0), (4, 4), (2, 9), (9, 8), (9, 9)] { print(p, describe(p.0, p.1)) }
func fall(_ n: Int) {
switch n {
case 5 where n > 9: print("a")
case 5: print("b"); fallthrough
case 6: print("c")
default: print("d")
}
}
fall(5); fall(6)
let opts: [Int?] = [1, nil, 3]
for case let x? in opts { print("x", x, terminator: " ") }
print()
for case .some(let x) in opts where x > 1 { print("big", x) }
if case 1...3 = 2, case "a"..."c" = "b" { print("ranges") }
struct Pt { var x: Int }
func ~= (pattern: Int, value: Pt) -> Bool { pattern == value.x }
switch Pt(x: 3) { case 3: print("custom ~="); default: print("no") }
- origin / pos x-axis 5 / x-axis / diagonal 4 / small x / even y / diagonal 9, then b c, c, x 1 x 3, big 3, ranges, custom ~=
- origin / pos x-axis 5 / x-axis / diagonal 4 / small x / even y / other, then b, c, x 1 x 3, big 3, ranges, no
- origin / pos x-axis 5 / pos x-axis -2 / diagonal 4 / small x / even y / diagonal 9, then b c, c c, x 1 x 3, big 3, ranges, custom ~=
Mental model. A sorting hat that stops at the first house that accepts you, even if a later house would fit better.
A switch takes the first case that matches, top to bottom, so order matters: (4, 4) is "diagonal" because that case comes before (1...3, _), (9, 9) is diagonal before it can be "even y", and (-2, 0) misses the first axis case (its where fails) and lands in the plain x-axis case. fallthrough jumps into the next case body without evaluating its pattern: fall(5) prints "b" then "c", fall(6) only "c".
for case let x? in opts and for case .some(let x) in opts where x > 1 filter and unwrap in the loop header. Range and tuple patterns use the ~= operator; defining ~= for your own type makes case 3: work on a Pt. The compiler checks enum switches are exhaustive but cannot warn about shadowed overlapping cases on integers, so reading order is your job.
First match wins; fallthrough skips the next pattern check.
Common traps
- Putting a general case first. It hides the specific ones.
- Expecting fallthrough to test the next condition. It does not.
They might follow up with
- Is a where clause part of exhaustiveness? No; a case with where does not count as covering its pattern.
Related questions
- 1.2.5 What is the difference between if case, guard case, and switch on an enum?
- 1.2.2 Why can switch on an Optional use .none / .some and also nil?
← What do nested optionals and nil assignments do: Int??, dictionaries… In what order do willSet and didSet run across a subclass, an… →