将您的问题重新构建为最小、完整且可验证的示例
首先请注意,如果您的问题包含一个最小的、完整的和可验证的示例 (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. 中的编译时错误吗?而且,这两个陈述真的等价吗?
上面函数中第一条guard
语句的编译时错误foo(...)
很能说明问题
布尔条件需要where
将其与变量绑定分开。
修复它:替换,
为where
这也在语言指南 - 基础 - 可选绑定中有所说明
您可以在单个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 }
}
然而,这两者并不真正等同。
guard
语句 2. 上面的语句允许我们在初始条件子句被证明是的情况下短路语句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 }
}
一个不太人为的例子是,如果我们想在后面的条件子句中使用成功绑定的变量 in(这里:作为参数)
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
子句。