保护条件引发讨论闭包的编译器错误



请考虑以下代码:

class Foo {
var a: Int
var b: Int
init(a: Int, b: String?) throws {
self.a = a
guard self.a > 0 else {
throw "Too little a!"
}
self.b = self.a
}
}
extension String: Error {}

很无厘头,但关键是它编译得很好。 现在将防护装置替换为:

guard b == nil || self.a > 0 else {

不是我们得到编译器错误!

错误:在所有成员初始化之前,闭包捕获了"self">

我一个人在任何地方都看不到关闭。如果条件是复合表达式,编译器是否将guard条件转换为闭包,从而引入错误(如果有闭包,则是正确的)?

错误或功能?

这是 Swift 3.0.2 的。

正如Martin在本问答中所解释的那样,问题在于||运算符是使用@autoclosure第二个参数实现的,以便允许短路计算(仅当左侧表达式的计算结果为false时才需要计算右手表达式)。

因此在表达式中

b == nil || self.a > 0

self.a > 0隐式地包裹在() -> Bool闭包中。这是有问题的,因为它需要捕获self,以便在应用闭包时可以访问a

但是,在初始化器中,Swift 严格限制了在完全初始化之前可以使用self执行的操作。其中一个限制是无法被闭包捕获 - 这就是你得到编译器错误的原因。

尽管实际上,闭包{ self.a > 0 }完全初始化之前捕获self并没有错,因为闭包所做的只是访问已经初始化的a。因此,这实际上只是一个边缘情况(有一个开放的错误报告),我希望这将在语言的未来版本中得到平滑。

在此之前,Martin 在此问答中所示的一种解决方案是使用临时局部变量来创建self.a值的副本,避免在闭包中捕获self

class Foo {
var a: Int
var b: Int
init(a: Int, b: String?) throws {
// some computation meaning that a != self.a
self.a = a * 42
// temporary local variable to be captured by the @autoclosure.
let _a = self.a
guard b == nil || _a > 0 else {
throw "Too little a!"
}
self.b = self.a
}
}
extension String: Error {}

显然,这假设self.a != a,否则您可以在guard条件中仅引用a而不是self.a

最新更新