Вы читаете перевод статьи Эрика Керни
Когда я впервые увидел оператор guard во время Apple’s Platform State of the Union, я не мог до конца понять, зачем бы я стал им пользоваться и что же он из себя представляет? Вот короткое описание:
Как и оператор if, guard исполняет код полагаясь на логическое значение выражения. В отличии от if, guard исполняет код только при получении false. Можно думать о нём как об Assert, только программа не будет завершена.
Вливаемся
Возьмём простой пример для сравнения старой техники и использования guard:
func fooManualCheck(x: Int?) { if x == nil || x <= 0 { // Условия не соблюдены, выходим из функции return } // Работаем с x x!.description }
Это самый простой способ (в стиле Objective-C) убедиться, что значение существует и удовлетворяет условию. Хотя, работает оно прекрасно, в нём есть несколько недостатков:
- Мы проверяем условие, которое нам не нужно, вместо проверки значения, которое нас интересует. Код становится очень запутанным, если у нас несколько таких проверок. Мы ведь надеемся, что наше условие на самом деле не пройдёт.
- Так же нужно “силой развернуть” (force unwrap) опциональное значение.
Swift представил нам более чистый способ сделать это и избавил нас от этих недостатков с помощью Optional Binding:
func fooBinding(x: Int?) { if let x = x where x > 0 { // Работаем с x x.description } // Условия не соблюдены, выходим из функции }
Этот вариант убирает оба недостатка старой функции, но добавляет один новый. Мы помещаем нужный нам код внутрь проверки условия, вместо того, чтобы писать его после. Поначалу проблему можно и не заметить, но можете представить, как запутанно это будет выглядеть, если вложить в функцию ещё несколько условий, которые нужно выполнить перед запуском кода.
Самый чистый способ — сперва проверить каждое условие и выйти, если одно из них не встречено. Это позволит легко понять, какие условия вызывают выход из функции.
И тут на помощь приходит guard:
func fooGuard(x: Int?) { guard let x = x where x > 0 else { // Условия не соблюдены, выходим из функции return } // Работаем с x x.description }
Использование guard решает все 3 проблемы, упомянутые выше:
- Идёт проверка условий, которые нам действительно важны. Если условие не соблюдено, запускается блок else, который обязательно выводит из функции. Если вы забудете постаивть return, компилятор сообщит вам об ошибке.
- Если условие соблюдено, опциональная переменная автоматически развёрнута и доступна внутри guard.
- Мы проверяем условия рано и функция будет просто читаема.
Классно ещё то, что это работает и для не-опциональных значений:
func fooNonOptionalGood(x: Int) { guard x > 0 else { // Условия не соблюдены, выходим из функции return } // Работаем с x } func fooNonOptionalBad(x: Int) { if x <= 0 { // Условия не соблюдены, выходим из функции return } // Работаем с x }
Сворачиваемся
Надеюсь, эта статья помогла вам понять, почему стоит начать использовать guard в своём коде как можно раньше. Ваш код сразу станет намного более простым для восприятия и дальнейших улучшений.