Несоответствия заявления гвардии
// Выполнение let first с последующей проверкой bool в выражении guard приводит к ошибке компиляции
self.action = { [weak self] in
guard let `self` = self, data.isJSON() else { return }
// Сначала выполняем проверку bool, а затем пусть работает
self.action = { [weak self] in
guard data.isJSON(), let `self` = self else { return }
Два утверждения выше кажутся мне эквивалентными. Почему это не сработает в первом случае?
1 ответ
Перестройте ваш вопрос в минимальный, полный и проверяемый пример.
Прежде всего, обратите внимание, что было бы предпочтительно, если бы ваш вопрос содержал минимальный, полный и поддающийся проверке пример (mvce), который в своей текущей форме не имеет:
- закрывающий список не имеет значения и только запутывает здесь
- неизвестный
self
(... без контекста), также, только сбивает с толку и не имеет отношения к этому вопросу
Вместо этого, ваш вопрос мог бы быть сконструирован так:
func foo(bar: Int?) {
// 1. why does this cause a compile time error?
guard let baz = bar, true else { return }
// 2. whereas this does not?
guard true, let bax = bar else { return }
}
Ответ ниже будет обсуждать этот mvce, а не скрытый пример в исходном вопросе. Наконец, отметим также, что guard
/ guard let
утверждение не является полностью релевантным (уникальным) по отношению к сути вопроса, так как мы видим такое же поведение для if
/ if let
заявления. Ответ ниже будет использовать guard
заявления.
Теперь, можем ли мы погасить ошибку времени компиляции в 1. выше? И эти два утверждения действительно эквивалентны?
Ошибка времени компиляции для 1-го guard
оператор в функции foo(...)
выше довольно показательно
Булево условие требует
where
отделить его от привязки переменной.Исправить это: заменить
,
сwhere
Об этом также говорится в руководстве по языку - Основы - Дополнительная привязка
Вы можете включить несколько дополнительных привязок в одном
if
Заявление и использоватьwhere
пункт, чтобы проверить наBoolean
состояние Если любое из значений в необязательных привязкахnil
илиwhere
пункт оцениваетfalse
вся необязательная привязка считается неудачной.
Следовательно, если мы хотим использовать условие-условие после необязательного связывания в guard
или же if
заявление, вам нужно использовать where
условие, чтобы отделить условие-предложение от предыдущей необязательной привязки.
func foo(bar: Int?) {
// 1. ok, compiles
guard let baz = bar where true else { return }
/* 2. or, include a conditional-clause prior to the
optional binding, but is this really equivalent..? */
guard true, let bax = bar else { return }
}
Эти два, однако, не совсем эквивалентны;
- утверждение 2. выше позволяет нам замкнуть
guard
заявление в случае, если начальная условная оговорка оказываетсяfalse
(затем не переходя к необязательному связыванию, а непосредственно вelse
блок изguard
заявление) - тогда как 1. выше допускает обратное: проверять условное предложение только в том случае, если успешное связывание выполнено успешно.
Естественно, есть случаи, когда мы предпочли бы одно над другим, например, если условное предложение содержит некоторые действительно тяжелые вычисления, мы можем не захотеть выполнять их, если не уверены, что дополнительное связывание выполнено успешно
let heavyStuff: () -> Bool = { print("foo"); /* ... */ return true }
func foo(bar: Int?) {
/* 1. call the heavyStuff boolean construct only if
the optional binding succeeds */
guard let baz = bar where heavyStuff() else { return }
/* 2. possibly unnesessarily perform heavy boolean
stuff prior to failing the optional binding */
guard heavyStuff(), let bax = bar else { return }
}
Менее надуманный пример, если мы хотим использовать успешно связанную переменную в (здесь: в качестве аргумента) в условном предложении, которое следует
let integerStuff: (Int) -> Bool = { _ in /* ... */ return true }
func foo(bar: Int?) {
/* 1. call the integerStuff boolean construct only if
the optional binding succeeds, using the binded
immutable as closure argument */
guard let baz = bar where integerStuff(baz) else { return }
/* 2. ... not really any good alternatives for such
flow if using this alternative */
guard integerStuff(baz ?? 0), let bax = bar else { return }
}
Наконец, обратите внимание, что технически, если вы действительно хотите отделить начальную необязательную привязку с помощью следующего условного предложения, вы можете использовать пустышку case let
(необязательное всегда успешное связывание / присваивание переменной) в сочетании с where
ключевое слово для вашего условного предложения
let checkThis: () -> Bool = { /* ... */ return true }
func foo(bar: Int?) {
// ...
guard let baz = bar, case let _ = () where checkThis() else { return }
}
Это, однако, просто чтобы показать эту техническую значимость; на практике просто используйте where
пункт.