switch
Представьте сортировочную машину на почте. Посылка едет по ленте, машина читает номер на наклейке и сбрасывает посылку в один из жёлобов. Над каждым жёлобом висит табличка: какие номера туда идут. Последний жёлоб подписан «всё остальное». Машину не запустят, пока для каждого номера не найдётся жёлоб.
Цепочка else if из прошлого урока тоже раскладывает значения по веткам, но в каждой ветке заново сравнивает одно и то же значение. Инструкция switch работает как сортировочная машина: берёт значение один раз и выполняет ветку case с подходящей табличкой:
let minutes = 450
let hour = minutes / 60
switch hour {
case 6, 7, 8:
print("breakfast") // breakfast
case 12, 13:
print("lunch")
case 18, 19, 20:
print("dinner")
default:
print("snack")
}Давайте пройдём по программе:
hourполучает450 / 60. Деление целых чисел отбрасывает остаток, поэтомуhourравен7: 450 минут от полуночи — это 7:30.switch hourберёт значение7и сверяет его с ветками сверху вниз.case 6, 7, 8:— семёрка в списке есть, и программа печатаетbreakfast.- Остальные ветки, в том числе
default, программа пропускает.
Ветки case
Ветка начинается словом case, за ним идёт табличка и двоеточие. Код ветки пишут под двоеточием, фигурные скобки ему не нужны. В этом уроке разберём два вида табличек.
Несколько значений
Значения перечисляют через запятую: case 6, 7, 8:. Ветка выполняется, если значение совпадает с любым из них. Тип значений тот же, что у выражения после switch, здесь это Int.
Диапазон
Вместо длинного списка можно написать диапазон через ... или ..<:
switch hour {
case 6...9:
print("breakfast") // breakfast
case 12...14:
print("lunch")
case 18...21:
print("dinner")
default:
print("snack")
}Ветка case 6...9: выполняется для любого часа от 6 до 9 включительно. Значения и диапазоны можно смешивать даже в одной ветке: case 0, 22...23:.
default и исчерпывающий switch
Ветка default выполняется, когда не подошёл ни один case. Это жёлоб «всё остальное».
В Swift switch должен быть исчерпывающим: для любого возможного значения обязана найтись ветка. Компилятор проверяет это до запуска, и не по текущему значению, а по типу. Покрывают ли значения и диапазоны Int все числа, компилятор не проверяет, поэтому ветки с ними никогда не считаются полными — даже case Int.min...Int.max:. Из-за этого switch по числу почти всегда заканчивается веткой default, а без неё не компилируется:
switch hour { // ✗
case 6...9:
print("breakfast")
case 12...14:
print("lunch")
}error: switch must be exhaustiveПод строкой с ошибкой компилятор печатает подсказку add a default clause, то есть «добавьте ветку default».
Только первая подходящая ветка
Машина сбрасывает посылку в первый подходящий жёлоб и сразу берётся за следующую. Так же и switch: проверяет ветки сверху вниз, выполняет первую подходящую и на этом заканчивается. Ветки ниже он уже не проверяет.
Здесь картинка с почтой неточна. На настоящей почте у номера ровно один жёлоб, а диапазоны в switch могут пересекаться, и тогда решает порядок веток. В программе ниже возраст 15 подходит и под 7...17, и под 15...25. Нажимайте «Следующий шаг» и следите, куда программа перейдёт после печати child:
1let year = 20262let born = 20113let age = year - born4switch age {5case 0...6:6 print("free")7case 7...17:8 print("child")9case 15...25:10 print("student")11default:12 print("adult")13}14print("done")
Константа year получает значение 2026.
После ветки case 7...17 программа сразу переходит к строке за switch. Ветка 15...25 не выполняется, хотя 15 в неё входит. Компилятор такие пересечения не запрещает, поэтому порядок веток выбираете вы.
В C и похожих на него языках выполнение в конце ветки проваливается в следующую, если не написать break. В Swift такого нет: ветка заканчивается сама, и break в конце не нужен.
Этот вопрос часто задают на собеседовании: чем switch в Swift отличается от switch в C? Хороший ответ называет три отличия:
- ветка не проваливается в следующую, и
breakв её конце не нужен; switchисчерпывающий: без ветки на каждое возможное значение он не компилируется;- в одном
caseможно перечислить несколько значений или написать диапазон.
Частая ошибка
Ночью программе делать нечего, и для часов 0...5 оставили пустую ветку:
switch hour {
case 0...5: // ✗
case 6...9:
print("breakfast")
default:
print("snack")
}error: 'case' label in a 'switch' must have at least one executable statementВ Swift ветка не может быть пустой: в ней должна быть хотя бы одна команда. Чтобы ветка ничего не делала, в неё пишут break: эта команда сразу завершает switch. Получится case 0...5: break.
Главное
switchберёт значение один раз и выполняет первую веткуcase, на которую оно подходит: по списку значений или по диапазону.switchзаканчивается сам сразу после ветки: в следующую ветку выполнение не проваливается, иbreakне нужен.switchисчерпывающий: компилятор не проверяет, покрывают ли значения и диапазоныIntвсе числа, поэтому безdefaultтакойswitchне компилируется.- Пустая ветка не компилируется; ветку, которая ничего не делает, записывают с
break.