Перейти к содержимому
Теория · Условия · урок 3 из 5

switch

Представьте сортировочную машину на почте. Посылка едет по ленте, машина читает номер на наклейке и сбрасывает посылку в один из жёлобов. Над каждым жёлобом висит табличка: какие номера туда идут. Последний жёлоб подписан «всё остальное». Машину не запустят, пока для каждого номера не найдётся жёлоб.

Цепочка else if из прошлого урока тоже раскладывает значения по веткам, но в каждой ветке заново сравнивает одно и то же значение. Инструкция switch работает как сортировочная машина: берёт значение один раз и выполняет ветку case с подходящей табличкой:

SWIFT
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")
}

Давайте пройдём по программе:

  1. hour получает 450 / 60. Деление целых чисел отбрасывает остаток, поэтому hour равен 7: 450 минут от полуночи — это 7:30.
  2. switch hour берёт значение 7 и сверяет его с ветками сверху вниз.
  3. case 6, 7, 8: — семёрка в списке есть, и программа печатает breakfast.
  4. Остальные ветки, в том числе default, программа пропускает.

Ветки case

Ветка начинается словом case, за ним идёт табличка и двоеточие. Код ветки пишут под двоеточием, фигурные скобки ему не нужны. В этом уроке разберём два вида табличек.

Несколько значений

Значения перечисляют через запятую: case 6, 7, 8:. Ветка выполняется, если значение совпадает с любым из них. Тип значений тот же, что у выражения после switch, здесь это Int.

Диапазон

Вместо длинного списка можно написать диапазон через ... или ..<:

SWIFT
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, а без неё не компилируется:

SWIFT
switch hour { // ✗
case 6...9:
    print("breakfast")
case 12...14:
    print("lunch")
}
TEXT
error: switch must be exhaustive

Под строкой с ошибкой компилятор печатает подсказку add a default clause, то есть «добавьте ветку default».

Только первая подходящая ветка

Машина сбрасывает посылку в первый подходящий жёлоб и сразу берётся за следующую. Так же и switch: проверяет ветки сверху вниз, выполняет первую подходящую и на этом заканчивается. Ветки ниже он уже не проверяет.

Здесь картинка с почтой неточна. На настоящей почте у номера ровно один жёлоб, а диапазоны в switch могут пересекаться, и тогда решает порядок веток. В программе ниже возраст 15 подходит и под 7...17, и под 15...25. Нажимайте «Следующий шаг» и следите, куда программа перейдёт после печати child:

ВживуюКакая ветка выполнитсяшаг 1 из 8 · рамок: 1

Константа year получает значение 2026.

Следите за подсвеченной строкой после печати child

После ветки case 7...17 программа сразу переходит к строке за switch. Ветка 15...25 не выполняется, хотя 15 в неё входит. Компилятор такие пересечения не запрещает, поэтому порядок веток выбираете вы.

В C и похожих на него языках выполнение в конце ветки проваливается в следующую, если не написать break. В Swift такого нет: ветка заканчивается сама, и break в конце не нужен.

Этот вопрос часто задают на собеседовании: чем switch в Swift отличается от switch в C? Хороший ответ называет три отличия:

  • ветка не проваливается в следующую, и break в её конце не нужен;
  • switch исчерпывающий: без ветки на каждое возможное значение он не компилируется;
  • в одном case можно перечислить несколько значений или написать диапазон.

Частая ошибка

Ночью программе делать нечего, и для часов 0...5 оставили пустую ветку:

SWIFT
switch hour {
case 0...5: // ✗
case 6...9:
    print("breakfast")
default:
    print("snack")
}
TEXT
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.