Рассмотрите ситуацию: определите, какой текст напечатает код и почему выполнение продолжится после первого совпавшего case.
let code = 1
switch code {
case 1:
print("один")
fallthrough
case 2:
print("два")
default:
print("другое")
}
Код напечатает один, затем два. После fallthrough Swift без повторной проверки передаёт управление в тело следующего case, поэтому условие case 2 фактически не сопоставляется со значением code.
Без явного fallthrough после выполнения case 1 конструкция switch завершилась бы.
Во многих языках конструкции switch исторически могли неявно переходить из одного case в следующий, что приводило к случайному выполнению нескольких ветвей. Swift выбрал безопасное поведение: после совпавшего case выполнение обычно завершается, а продолжение нужно обозначать явно через fallthrough.
Такой подход сохраняет компактность сценариев, где последовательное выполнение действительно нужно, но делает его заметным при чтении кода.
fallthrough легко принять за повторное сопоставление следующего шаблона. Это неверно: переход выполняется без проверки значения и без анализа условия следующей ветви.
Ошибка особенно опасна, если следующий case содержит логику, которая предполагает успешное соответствие шаблону. При использовании fallthrough нужно рассуждать не о новом совпадении, а о безусловном переходе к следующему текстовому case.
Сначала code сравнивается с шаблоном 1, поэтому выполняется тело первого case и печатается один. Затем fallthrough передаёт управление непосредственно следующему case, то есть ветви case 2.
Шаблон 2 заново не проверяется: несмотря на то что code равно 1, выполняется print("два"). После завершения тела этой ветви switch заканчивается; автоматического перехода в default не происходит.
fallthrough действует только на следующий текстовый case. Он не позволяет перейти через одну ветвь к произвольной последующей. Если последовательное выполнение нужно для нескольких значений, часто понятнее объединить шаблоны: case 1, 2:.
Главный компромисс — явность против гибкости. fallthrough полезен для намеренной цепочки действий, но усложняет локальное понимание ветвей, поэтому обычно предпочтительнее вынести общую логику в функцию или явно объединить шаблоны.
Предположим, при обработке состояния заказа нужно выполнить общую операцию для состояний paid и shipped. Вариант с fallthrough может выглядеть так: ветка paid выполняет свою логику, а затем передаёт управление shipped. Минус — читателю приходится помнить, что второй шаблон не проверяется.
Вариант с объединёнными шаблонами проще, если действия одинаковы:
Если действия различаются, лучше вызвать общую функцию из двух ветвей или явно записать общий шаг. fallthrough стоит выбирать только тогда, когда именно последовательный переход является частью алгоритма; это уменьшает риск скрытого выполнения неподходящей логики.
Проверяется ли условие следующего case после fallthrough?
Нет. fallthrough не запускает новый цикл сопоставления и не проверяет следующий шаблон. Управление сразу передаётся его телу, поэтому даже несовпадающее значение не предотвращает выполнение этой ветви.
Чем fallthrough отличается от записи case 1, 2:?
При case 1, 2: оба значения выбирают одну и ту же ветвь, а выполняется она только один раз после успешного сопоставления. При fallthrough сначала выполняется тело одного case, затем тело следующего, то есть это последовательность действий, а не объединение условий.
Продолжится ли выполнение после ветви, в которую попали через fallthrough, в default?
Нет. После завершения тела целевого case Swift покидает switch, как при обычном совпадении. default выполняется только если ни один шаблон не совпал обычным способом; fallthrough не превращает его в следующую автоматически исполняемую ветвь.