保护条件引发编译器错误,讨论关闭
Guard condition provokes compiler error that talks about closure
考虑这段代码:
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 {
不是我们遇到编译器错误!
Error: 'self' captured by a closure before all members were initialized
我个人在任何地方都看不到闭包。如果 guard
条件是复合表达式,编译器是否将它们翻译成闭包,从而引入错误(如果 是 闭包,这将是正确的)?
错误或功能?
这是 Swift 3.0.2.
这个问题,正如 Martin , is that the ||
operator is implemented with an @autoclosure
第二个参数所解释的那样,为了允许进行短路评估(仅当左侧表达式的计算结果为 false
时,才需要对右侧表达式进行评估).
因此在表达式中
b == nil || self.a > 0
self.a > 0
隐含在 () -> Bool
闭包中。这是有问题的,因为它需要捕获 self
,以便在应用闭包时可以访问 a
。
但是,在初始化程序中,Swift 严格限制了在 self
完全初始化之前您可以对其执行的操作。这些限制之一是无法被闭包捕获——这就是为什么会出现编译器错误。
虽然实际上,闭包 { self.a > 0 }
在完全初始化之前捕获 self
并没有错,因为闭包所做的只是访问它上面的 a
,这已经初始化。因此,这实际上只是一个边缘案例(there's an open bug report),我希望在该语言的未来版本中能够解决这个问题。
在那之前,如 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
。
考虑这段代码:
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 {
不是我们遇到编译器错误!
Error: 'self' captured by a closure before all members were initialized
我个人在任何地方都看不到闭包。如果 guard
条件是复合表达式,编译器是否将它们翻译成闭包,从而引入错误(如果 是 闭包,这将是正确的)?
错误或功能?
这是 Swift 3.0.2.
这个问题,正如 Martin ||
operator is implemented with an @autoclosure
第二个参数所解释的那样,为了允许进行短路评估(仅当左侧表达式的计算结果为 false
时,才需要对右侧表达式进行评估).
因此在表达式中
b == nil || self.a > 0
self.a > 0
隐含在 () -> Bool
闭包中。这是有问题的,因为它需要捕获 self
,以便在应用闭包时可以访问 a
。
但是,在初始化程序中,Swift 严格限制了在 self
完全初始化之前您可以对其执行的操作。这些限制之一是无法被闭包捕获——这就是为什么会出现编译器错误。
虽然实际上,闭包 { self.a > 0 }
在完全初始化之前捕获 self
并没有错,因为闭包所做的只是访问它上面的 a
,这已经初始化。因此,这实际上只是一个边缘案例(there's an open bug report),我希望在该语言的未来版本中能够解决这个问题。
在那之前,如 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
。