Re-constructing your question into a minimal, complete and verifiable example
First of all note that it would be preferable if your question contained a minimal, complete and verifiable example (mvce), which, in its current form, it does not:
- the closure list is irrelevant and only confusing here
- the unknown
self
(... no context) is, likewise, only confusing and of no relevance to this question
Instead, a mvce of your question could've bee constructed along the lines of:
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 }
}
The answer below will discuss this mvce, rather than the obscured example in the original question. Finally note also that the guard
/guard let
statement is not entirely relevant (unique) w.r.t. the core of the question, as we see the same behaviour for if
/if let
statements. The answer below will use guard
statements.
Now, can we redeem the compile time error in 1. above? And, are these two statements really equivalent?
The compile time error for the 1st guard
statement in the function foo(...)
above is quite telling
Boolean condition requires where
to separate it from variable binding.
Fix it: replace ,
with where
This is also stated in the Language Guide - The Basics - Optional Binding
You can include multiple optional bindings in a single if
statement
and use a where
clause to check for a Boolean
condition. If any of the values in the optional bindings are nil
or the where
clause
evaluates to false
, the whole optional binding is considered
unsuccessful.
Hence, if we want to use a condition-clause after optional binding in an guard
or if
statement, you need to use a where
clause to separate the condition-clause from the preceding optional binding.
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 }
}
These two are, however, not really equivalent;
- statement 2. above allows us to short-circuit the
guard
statement in case the initial conditional-clause turns out to be false
(then not proceeding to the optional binding, but rather directly into the else
block of the guard
statement)
- whereas 1. above allows the reverse: check the conditional-clause only if the optional binding succeeds.
There are naturally cases in which we would prefer one over the other, e.g. if the conditional-clause contains some really heavy calculations, we might not want to execute these unless we're certain that the optional binding succeeds
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 }
}
A less contrived example is if we want to use a successfully binded variable in (here: as an argument) in the conditional-clause that follows
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 }
}
Finally note that, technically, if you really want to separate an initial optional binding with a following conditional-clause, you could use dummy case let
(non-optional always-succeeding variable binding/assignment) statement coupled with a where
keyword for your conditional-clause
let checkThis: () -> Bool = { /* ... */ return true }
func foo(bar: Int?) {
// ...
guard let baz = bar, case let _ = () where checkThis() else { return }
}
This is, however, just to show this technicality; in practice, just use a where
clause.