Kamil Nomtek.com
Kamil Nomtek.com

Reputation: 1650

Strange "'self' captured by a closure before all members were initialized" error

Please take a look at the following code:

class A {
    let a: String
    let b: String

    init(a: String, b: String) {
        self.a = a
        self.b = b
    }
}

class B: A {
    let c: Bool

    private let aExpectedValue = "a"
    private let bExpectedValue = "b"

    override init(a: String, b: String) {
        c = (a == aExpectedValue && b == bExpectedValue)
        super.init(a: a, b: b)
    }
}

This causes an error in B.init:

error

However, if I change it either to c = (a == aExpectedValue) or c = (b == bExpectedValue) then it compiles correctly.

Does anybody know why is that?

Upvotes: 6

Views: 1588

Answers (2)

Rob Napier
Rob Napier

Reputation: 299295

The problem is in bExpectedValue. That's an instance property on B. That interacts with the definition of && on Bool:

static func && (lhs: Bool, rhs: @autoclosure () throws -> Bool) rethrows -> Bool

The @autoclosure makes the b == bExpectedValue into a closure, capturing it as self.bExpectedValue. That's not allowed before initialization is complete. (The closure here is to allow short-circuiting. The rhs closure is not evaluated if lhs is false.)

This is pretty awkward (see SR-944 that MartinR references for a little discussion about it).

If bExpectedValue were static, or if it were moved outside the class definition, then this wouldn't be an issue. The following approach will also fix it:

override init(a: String, b: String) {
    let goodA = a == aExpectedValue
    let goodB = b == bExpectedValue
    c = goodA && goodB
    super.init(a: a, b: b)
}

Upvotes: 15

Agisight
Agisight

Reputation: 1818

You need to create a new initializer with another vars or call super.init(a:, b:) before any expression with this properties.

Call this:

override init(a: String, b: String) {
    super.init(a: a, b: b)
    c = (a == aExpectedValue && b == bExpectedValue)        
}

or change it to:

init(newA: String, newB: String) {
    c = (newA == aExpectedValue && newB == bExpectedValue)        
    super.init(a: newA, b: newB)
}

Upvotes: 0

Related Questions