The problem, as explained by Martin in this Q&A, is that the ||
operator is implemented with an @autoclosure
second parameter, in order to allow for short-circuit evaluation (the right hand expression need only be evaluated if the left hand expression evaluates to false
).
Therefore in the expression
b == nil || self.a > 0
self.a > 0
is implicitly wrapped in a () -> Bool
closure. This is problematic, because it requires the capture of self
, so that a
can be accessed upon applying the closure.
However, in an initialiser, Swift tightly restricts what you can do with self
before it has been fully initialised. One of these restrictions is the inability to be captured by a closure – which is why you get a compiler error.
Although really, there's nothing wrong with the closure { self.a > 0 }
capturing self
before it's fully initialised, because all the closure is doing is accessing a
on it, which has already been initialised. Therefore, this is really just an edge case (for which there's an open bug report), which I hope will be smoothed out in a future version of the language.
Until then, one solution as shown by Martin in this Q&A, is to use a temporary local variable to create a copy of the value of self.a
, avoiding the capturing of self
in the closure:
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 {}
Obviously, this assumes that self.a != a
, otherwise you can just refer to a
instead of self.a
in the guard
condition.