Guard条件引发编译器错误,该错误涉及关闭

时间:2017-03-28 15:58:48

标签: swift compiler-errors swift-guard

考虑以下代码:

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。

1 个答案:

答案 0 :(得分:2)

正如马丁in this Q&A所解释的那样,问题是|| operator是用@autoclosure秒参数实现的,以便进行短路评估(右手表达式)只有在左手表达式求值为false时才需要进行评估。

因此在表达式中

b == nil || self.a > 0

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

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

尽管如此,在{ self.a > 0 }完全初始化之前,self关闭a没有任何问题,因为所有关闭操作都是访问它self.a,它已经被初始化了。因此,这实际上只是一个边缘情况(there's an open bug report),我希望将来会在该语言的未来版本中进行平滑。

在此之前,Martin in this Q&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封闭:

a

显然,这假定为self.a,否则您只能在guard条件中引用{{1}}而不是{{1}}。