質問を最小限で完全で検証可能な例に再構築する
まず第一に、質問に最小限で完全かつ検証可能な例 (mvce)が含まれていることが望ましいことに注意してください。現在の形式では、次のようにはなりません。
- クロージャーリストは無関係であり、ここで混乱するだけです
self
同様に、未知(... コンテキストなし) は混乱を招くだけであり、この質問とは関係ありません。
代わりに、あなたの質問の mvce は、次の行に沿って構築された可能性があります。
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. のコンパイル時エラーを引き換えることはできますか? そして、これら2つのステートメントは本当に同等ですか?
guard
上記の関数の最初のステートメントのコンパイル時エラーfoo(...)
は非常にわかりやすい
ブール条件ではwhere
、変数バインディングから分離する必要があります。
修正: に置き換え,
ますwhere
これは、Language Guide - The Basics - Optional Bindingにも記載されています。
複数のオプションのバインディングを 1 つのif
ステートメント
に含め、where
句を使用してBoolean
条件をチェックできます。オプションのバインディングのいずれかの値が であるnil
か、where
句が に評価された場合、オプションのバインディングfalse
全体が失敗したと見なされます。
したがって、orステートメントでオプションのバインディングの後に条件句を使用する場合は、条件句を前のオプションのバインディングから分離する句を使用する必要があります。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 つは実際には同等ではありません。
- ステートメント 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
句を使用するだけです。