guard let
Представьте, что вы собираетесь в гости и первым делом заглядываете в коробку с подарком. Если она пуста, идти незачем и вы сразу отменяете поход. Если подарок на месте, вы берёте его с собой и больше о проверке не думаете.
Имя из if let живёт только внутри фигурных скобок, поэтому весь код, которому нужно значение, приходится писать внутри них. С каждой новой проверкой он уходит всё дальше вправо. Функции удобнее другой порядок: проверить всё в самом начале и сразу выйти, если чего-то не хватает. Для этого в Swift есть guard:
func greeting(_ text: String) -> String {
guard let age = Int(text) else {
return "not a number"
}
guard age >= 0 else {
return "negative age"
}
return "next year you turn \(age + 1)"
}
print(greeting("30")) // next year you turn 31
print(greeting("abc")) // not a number
print(greeting("-5")) // negative ageДавайте разберём три вызова:
greeting("30"): в тексте есть число, первыйguardпропускает дальше, и константаageравна30. Второйguardтоже пропускает:30не меньше нуля. Функция возвращаетnext year you turn 31.greeting("abc"): числа нет,Int(text)даётnil, и выполняетсяelseпервогоguard.returnсразу завершает функцию, и до остальных строк дело не доходит.greeting("-5"): число есть, но второйguardвидит, что оно меньше нуля, и егоelseвозвращаетnegative age.
Как устроена запись
Строка guard let age = Int(text) else { … } состоит из трёх частей:
guard— «убедись, что…»;let age = Int(text)— та же привязка опционала, что вif let;else { … }— блок, который выполняется, только когда в коробкеnil.
Такой приём называют ранним выходом: функция заканчивает работу, как только видит, что продолжать нельзя. Все проверки стоят в начале, а основной код идёт ниже, у левого края, без лишних фигурных скобок.
Что даёт guard
Имя живёт после guard
У if let имя существует внутри фигурных скобок, а у guard let — после всей конструкции, до конца блока, где стоит guard. Чаще всего это вся функция. В примере константа age объявлена в первом guard, а используется и во втором guard, и в последнем return.
else обязан выйти
Пройти ниже guard можно только со значением, поэтому else должен уйти и не пустить выполнение дальше. Из знакомых вам команд для этого подходят:
- в функции —
return; - в цикле —
continue, чтобы перейти к следующей итерации, илиbreak, чтобы завершить цикл.
Здесь картинка с подарком неточна. В жизни, увидев пустую коробку, можно махнуть рукой и всё равно пойти в гости. guard так не разрешает: если else не выходит, программа не скомпилируется.
guard с обычным условием
После guard может стоять не только let, но и любое условие типа Bool, как второй guard в примере. Если условие ложно, выполняется else. В цикле так отбрасывают неподходящие значения:
for n in 1...6 {
guard n % 3 != 0 else {
continue
}
print(n, terminator: " ") // 1 2 4 5
}guard читается как «нужно, чтобы n не делилось на 3». Для 3 и 6 условие ложно и continue переходит к следующему числу, не доходя до print.
guard let или if let
Этот вопрос часто задают на собеседовании: «Чем guard let отличается от if let?» Отличия удобно держать в таблице:
if let | guard let | |
|---|---|---|
| Где живёт имя | внутри фигурных скобок | после guard, до конца блока |
Ветка else | необязательна, может делать что угодно | обязательна и должна выйти |
| Когда удобен | значение нужно в одном месте | проверка в начале функции |
Выбирайте guard, когда без значения продолжать нет смысла, а if let — когда у программы есть осмысленный путь и для nil.
Частая ошибка
В else напечатали сообщение, но не вышли:
func showAge(_ text: String) {
guard let age = Int(text) else {
print("not a number")
} // ✗
print(age)
}error: 'guard' body must not fall through, consider using a 'return' or 'throw' to exit the scopeПрограмма не компилируется: после print выполнение пошло бы ниже guard, а там age без значения. В подсказке компилятора есть знакомый вам способ выйти — return. Допишите его после print("not a number"). В функции без -> инструкцию return пишут без значения: она только завершает функцию.
Главное
guard letоткрывает опционал, а имя из него живёт послеguardдо конца блока — чаще всего функции.- Ветка
elseуguardобязательна и должна выйти: например, черезreturnв функции, черезcontinueилиbreakв цикле. guardпроверяет и обычное условие: при ложном выполняетсяelse.guardудобен для проверок в начале функции: основной код идёт ниже без вложенных скобок.