Patterns in switch
In the previous lesson, each chute of the sorting machine had a sign with numbers or a range. Sometimes that is not enough: you need a chute for "every number below zero", and those cannot be listed. Then an inspector stands at the chute: the inspector looks at the parcel and decides whether to let it in.
The sign after case is called a pattern: a value either fits it or does not. A list of values and a range are patterns you already know. Here is a branch with an inspector:
let morning = -9
let noon = 1
let average = (morning + noon) / 2
switch average {
case let t where t < 0:
print("frost \(t)") // frost -4
case 0...15:
print("cool")
default:
print("warm")
}Let's walk through the program:
averagegets(-9 + 1) / 2, which is-4.case let t where t < 0:puts-4into a new constanttand checks the conditiont < 0.- The condition is true, so this branch runs: the program prints
frost -4. - The program skips the
0...15anddefaultbranches.
A constant in a pattern and where
The branch case let t where t < 0: has two parts.
case let
The pattern let t fits any value and puts it into a new constant t. This constant is visible only inside its own branch. There you use it like any other constant: in the example it goes into a string through \(t).
where
After where you write a condition of type Bool. The branch runs only if the pattern fits and the condition is true. If the condition is false, the switch moves on to the next branch as if the pattern had not fit.
That is the inspector at the chute: the sign let t lets any parcel through, while where t < 0 lets in only negative numbers.
A switch on a string
A switch works with more than numbers. Its branches can compare a string with strings written in advance:
let command = "stop"
switch command {
case "start":
print("starting")
case "stop", "pause":
print("stopping") // stopping
default:
print("unknown command")
}What matters about such a switch:
- a string is compared as a whole and case-sensitively:
"Stop"does not fitcase "stop"; - several strings in one branch are listed with commas, like numbers;
- there are endlessly many strings, so a
switchon aStringneedsdefaulttoo.
fallthrough
Sometimes falling into the next branch is exactly what you want. Then you write the word fallthrough at the end of a branch:
let role = "editor"
switch role {
case "admin":
print("delete posts")
fallthrough
case "editor":
print("edit posts") // edit posts
fallthrough
default:
print("read posts") // read posts
}Let's see how fallthrough works:
fallthroughjumps to the start of the next branch without checking that branch's pattern: withrole == "editor"the program printsedit postsand thenread postsfromdefault;- with
"admin"the program would print all three lines, and with any other role onlyread posts; fallthroughcannot be written in the last branch: there is nowhere to go.
You rarely need fallthrough: several values in one branch are usually enough.
Common mistake
Two branches with where cover every number between them, but default was left out:
switch average { // ✗
case let t where t < 0:
print("below zero")
case let t where t >= 0:
print("zero or above")
}error: switch must be exhaustiveA person sees that any number is either below zero or not. The compiler, however, does not compare where conditions with each other and assumes each such branch may fail to fit.
Here the inspector picture is not exact: real inspectors could agree among themselves, while a where condition knows only about its own branch. That is why the last branch is written as default: it takes everything the branches above did not let through.
Key points
- The pattern
let tin acaseputs the value into a new constant that is visible only inside the branch. whereadds a condition to a branch; the compiler does not compare such conditions with each other, so branches withwheredo not replacedefault.- A
switchon a string compares it as a whole, case-sensitively, and needsdefaulttoo. fallthroughmoves into the next branch without checking its pattern.