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

Шаблоны в switch

В прошлом уроке над каждым жёлобом сортировочной машины висела табличка с номерами или диапазоном. Иногда этого мало: нужен жёлоб «все числа меньше нуля», а перечислить их нельзя. Тогда у жёлоба ставят контролёра: он смотрит на посылку и решает, пускать ли её внутрь.

Табличку после case называют шаблоном: значение либо подходит под него, либо нет. Список значений и диапазон — уже знакомые шаблоны. Вот ветка с контролёром:

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

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

  1. average получает (-9 + 1) / 2, то есть -4.
  2. case let t where t < 0: кладёт -4 в новую константу t и проверяет условие t < 0.
  3. Условие истинно, поэтому выполняется эта ветка: программа печатает frost -4.
  4. Ветки 0...15 и default программа пропускает.

Константа в шаблоне и where

Ветка case let t where t < 0: состоит из двух частей.

case let

Шаблон let t подходит под любое значение и кладёт его в новую константу t. Эта константа видна только внутри своей ветки. Там с ней работают как с любой другой константой: в примере она попадает в строку через \(t).

where

После where пишут условие типа Bool. Ветка выполняется, только если шаблон подошёл и условие истинно. Если условие ложно, switch идёт к следующей ветке, как будто шаблон не подошёл.

Это и есть контролёр у жёлоба: табличка let t пускает любую посылку, а where t < 0 пускает внутрь только отрицательные числа.

switch по строке

switch работает не только с числами. Ветки могут сравнивать строку с заранее записанными строками:

SWIFT
let command = "stop"
switch command {
case "start":
    print("starting")
case "stop", "pause":
    print("stopping") // stopping
default:
    print("unknown command")
}

Что важно знать о таком switch:

  • строка сравнивается целиком и с учётом регистра: "Stop" под case "stop" не подойдёт;
  • несколько строк в одной ветке перечисляют через запятую, как числа;
  • строк бесконечно много, поэтому switch по String тоже требует default.

fallthrough

Иногда выполнение должно намеренно провалиться в следующую ветку. Тогда в конце ветки пишут слово fallthrough:

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

Разберём, как работает fallthrough:

  • fallthrough переходит в начало следующей ветки и не проверяет её шаблон: при role == "editor" программа печатает edit posts, а затем read posts из default;
  • при "admin" программа напечатала бы все три строки, а при любой другой роли — только read posts;
  • в последней ветке fallthrough писать нельзя: переходить некуда.

Нужен fallthrough редко: чаще хватает нескольких значений в одной ветке.

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

Две ветки с where вместе покрывают все числа, но default не написали:

SWIFT
switch average { // ✗
case let t where t < 0:
    print("below zero")
case let t where t >= 0:
    print("zero or above")
}
TEXT
error: switch must be exhaustive

Человек видит, что любое число либо меньше нуля, либо нет. А компилятор условия после where между собой не сравнивает и считает, что каждая такая ветка может не подойти.

Здесь картинка с контролёрами неточна: настоящие контролёры могли бы договориться между собой, а условие where знает только о своей ветке. Поэтому последнюю ветку пишут как default: она примет всё, что не подошло под ветки выше.

Главное

  • Шаблон let t в case кладёт значение в новую константу, которая видна только внутри ветки.
  • where добавляет к ветке условие; компилятор не сравнивает такие условия между собой, поэтому ветки с where не заменяют default.
  • switch по строке сравнивает её целиком, с учётом регистра, и тоже требует default.
  • fallthrough переходит в следующую ветку, не проверяя её шаблон.