Зачем нужен switch
Когда вариантов больше двух-трёх, цепочка if / else if начинает разваливаться: условия повторяются, легко забыть один из вариантов, а код растягивается вширь. Представьте турникет в метро, который должен по-разному реагировать на цвет карты — зелёный, жёлтый, красный, синий, — и ещё как-то обработать карту неизвестного цвета. Писать это через if можно, но неудобно читать: непонятно, все ли варианты учтены. Для таких задач в Swift есть switch — оператор ветвления, который сравнивает одно значение с набором образцов и сразу показывает, что вариантов учтено ровно столько, сколько нужно.
Официальный термин: сопоставление с образцом
switch в Swift — это не просто «переключатель по значению», как в некоторых языках. Каждый case — это образец (pattern), и Swift проверяет, подходит ли под этот образец значение в switch. Образцом может быть конкретное значение, диапазон, кортеж или значение с дополнительным условием. Из-за этого механизм называют сопоставлением с образцом (pattern matching) — он гораздо мощнее, чем «сравнить с числом».
let signal = "жёлтый"
switch signal {
case "красный":
print("Стоять")
case "жёлтый":
print("Приготовиться")
case "зелёный":
print("Идти")
default:
print("Неизвестный сигнал")
}
Swift сравнивает signal по очереди с каждым case сверху вниз и выполняет код первого совпавшего варианта. Здесь совпадёт "жёлтый", напечатается Приготовиться, и на этом работа switch закончится.
Исчерпывающий switch и роль default
Swift требует, чтобы switch был исчерпывающим: обязан быть учтён каждый возможный вариант значения. Для строки заранее неизвестно, какие символы введёт пользователь, поэтому компилятор не может проверить все строки — и здесь нужен default, который ловит всё, что не подошло под предыдущие case. Без него код выше просто не скомпилируется.
Для некоторых типов Swift сам видит, что варианты уже покрыты полностью, и default не нужен — например, если case с диапазонами вместе покрывают весь Int без исключений. Но в подавляющем большинстве уроков про базовые типы проще и надёжнее всегда добавлять default: он делает исчерпываемость видимой и защищает от случая, который вы не предусмотрели.
switch не проваливается сам, в отличие от C
Если раньше вы видели код на C, C++ или Java, там после совпавшего case выполнение по умолчанию «проваливается» в следующий case, если не поставить break. В Swift всё наоборот: как только совпал один case, выполняется только его код, а затем switch завершается сам, без всякого break. Именно поэтому пример с турникетом выше и работает: совпадение с "жёлтый" не потащило за собой ветку "зелёный".
Иногда провал в следующий case всё-таки нужен — это редкий случай, но для него есть отдельное ключевое слово fallthrough, которое явно говорит: «выполни ещё и код следующего case, без проверки его образца».
let day = 6
switch day {
case 6:
print("Суббота — выходной")
fallthrough
case 7:
print("Можно поспать подольше")
default:
print("Обычный день")
}
Здесь при day == 6 напечатаются обе строки: сначала код для субботы, а затем — из-за fallthrough — код случая 7, без проверки, что day действительно равен 7. Используйте fallthrough только когда логика правда требует «и это тоже», иначе он делает код менее предсказуемым.
Несколько значений в одном case
Если несколько значений должны приводить к одному и тому же результату, их можно перечислить через запятую в одном case — это не то же самое, что fallthrough: здесь один case просто описывает несколько образцов сразу.
let letter: Character = "a"
switch letter {
case "a", "e", "i", "o", "u":
print("гласная")
default:
print("согласная")
}
Интервалы в case
Диапазоны, которые вы уже видели у операторов (..< и ...), прекрасно работают как образцы: case совпадёт, если значение попадает в диапазон.
let score = 73
switch score {
case 90...100:
print("пятёрка")
case 75..<90:
print("четвёрка")
case 60..<75:
print("тройка")
default:
print("нужно подтянуть тему")
}
score равен 73, это входит в интервал 60..<75, поэтому напечатается тройка. Порядок case здесь важен: проверка идёт сверху вниз, и если бы диапазоны пересекались, выиграл бы тот, что стоит выше.
Кортежи, подстановочный знак и привязка значения
В case можно сопоставлять сразу кортеж — это удобно, когда результат зависит от нескольких значений вместе, а не по отдельности. Подстановочный знак _ в позиции кортежа означает «здесь подойдёт что угодно, значение не важно».
let point = (0, 5)
switch point {
case (0, 0):
print("это начало координат")
case (_, 0):
print("точка на оси X")
case (0, _):
print("точка на оси Y")
default:
print("точка вне осей")
}
Здесь point — это (0, 5), первый элемент кортежа равен нулю, поэтому совпадёт case (0, _), и напечатается точка на оси Y.
Кроме подстановочного знака, значение из кортежа можно привязать к временной константе прямо в образце с помощью let — тогда внутри тела case можно им пользоваться.
let another = (3, 0)
switch another {
case (0, 0):
print("начало координат")
case (let x, 0):
print("точка (\(x), 0) лежит на оси X")
default:
print("другая точка")
}
Второй элемент кортежа — ноль, а первый образец привязки let x захватывает значение 3 в константу x, которую можно использовать в теле case.
Условие where: сужаем образец
Иногда самого образца не хватает — нужно ещё и дополнительное условие поверх привязанного значения. Для этого после образца пишут where с обычным булевым выражением. case сработает, только если совпал и образец, и условие where.
let temperature = -5
switch temperature {
case let t where t < 0:
print("мороз, \(t) градусов")
case 0:
print("ровно ноль")
case let t where t > 30:
print("жара, \(t) градусов")
default:
print("обычная погода")
}
where особенно полезен вместе с привязкой из кортежа, когда результат зависит от связи между двумя значениями, а не от каждого отдельно.
let pair = (4, 4)
switch pair {
case let (a, b) where a == b:
print("значения равны")
case let (a, b) where a > b:
print("первое больше")
default:
print("первое меньше")
}
Сопоставление строк
Строки в case сравниваются целиком и с учётом регистра, точно так же, как оператор == у строк. Никакой магии с частичным совпадением здесь нет — если нужно проверить, например, что строка лишь начинается с чего-то, для этого по-прежнему используют методы строк, а не switch.
let command = "старт"
switch command {
case "старт", "пуск":
print("Запускаем")
case "стоп":
print("Останавливаем")
default:
print("Команда не распознана")
}
switch как выражение
Начиная со Swift 5.9, switch можно использовать не только как оператор с блоками кода, а как выражение, которое сразу возвращает значение — тогда каждый case вместо инструкций содержит одно значение соответствующего типа.
let age = 10
let category = switch age {
case 0..<18: "ребёнок"
case 18..<65: "взрослый"
default: "пенсионер"
}
print(category)
Это просто более короткая запись той же идеи: получить одно из нескольких значений в зависимости от условия, без промежуточной переменной и return в отдельной функции.
Частые ошибки
- Забыть
defaultтам, где Swift не может сам доказать исчерпываемость (почти всегда дляStringи часто дляInt) — компилятор сразу укажет на ошибку, это не страшно, но замедляет работу, если не знать причину. - Ждать автоматического «провала» в следующий
case, как в C — в Swift этого не происходит, а неожиданный провал случится только если сами написалиfallthrough. - Перепутать порядок диапазонов или образцов — раз проверка идёт сверху вниз, слишком широкий образец наверху «съест» те случаи, которые должны были попасть в более узкий образец ниже.
- Забыть, что привязанная через
letпеременная существует только внутри своегоcase— использовать её в другой ветке или послеswitchнельзя.
Резюме
switchсравнивает значение с набором образцов и выполняет код первого совпавшегоcase.switchобязан быть исчерпывающим;defaultловит всё, что не подошло под явныеcase.- В отличие от C, выполнение автоматически не проваливается в следующий
case; для явного провала естьfallthrough. - Несколько значений можно перечислить через запятую в одном
case, а интервалы задаются через..<и.... - Кортежи сопоставляются целиком,
_означает «что угодно», аletв образце привязывает значение к константе. whereдобавляет к образцу дополнительное условие.- Начиная со Swift 5.9
switchможно использовать как выражение, сразу возвращающее значение.
Проверьте себя
4 вопроса
Светофор по цвету
Напишите функцию trafficLightAction(_:), которая принимает строку с названием цвета сигнала светофора и возвращает подсказку водителю.
Правила:
"красный"или"бордовый"— вернуть"Стоять";"жёлтый"— вернуть"Приготовиться";"зелёный"— вернуть"Идти";- "Неизвестный сигнал".
Используйте switch по строке, а для первого правила укажите оба значения в одном case через запятую.
Возрастная категория
Напишите функцию ageCategory(_:), которая принимает возраст человека (Int, всегда неотрицательный) и возвращает его категорию по следующим границам:
- от 0 до 2 лет включительно —
"малыш"; - от 3 до 6 лет включительно —
"дошкольник"; - от 7 до 17 лет включительно —
"школьник"; - от 18 до 64 лет включительно —
"взрослый"; - 65 лет и старше —
"пенсионер".
Используйте switch с диапазонами в качестве образцов case, а последний вариант оформите через default.
Четверть координатной плоскости
Напишите функцию quadrant(_:_:) с двумя параметрами x и y типа Int, которая определяет положение точки (x, y) на координатной плоскости и возвращает строку:
(0, 0)—"начало координат";- "ось X";
- "ось Y";
- "первая четверть";
- "вторая четверть";
- "третья четверть";
- "четвёртая четверть".
Используйте один switch по кортежу (x, y), привязку значений через let и условие where там, где одного образца недостаточно.