Sergey Voitovich Sergey Voitovich - 3 years ago 36
Swift Question

What caused 'Constant captured by a closure before being initalized' error

In following class

class Foo {
let _defaultValue = "N/A"
let value: String

init (dict: NSDictionary) {
self.value = dict["bar"] as? String! ?? _defaultValue
}
}


compilation fails with message
constant 'self.value' captured by a closure before being initialized


As far as I can see no operators reads
self.value
and this message is really confusing.

The workaround I randomly invented confuses me even more:

class Foo {
let value: String

init (dict: NSDictionary) {
let _defaultValue = "N/A"
self.value = dict["bar"] as? String! ?? _defaultValue
}
}


Making
_defaultValue
declared and initialized inside constructor shuts compiler complains up and program works!

How to explain such thing?

Answer Source

The reason for the error message is that the nil-coalescing operator is defined as

public func ??<T>(optional: T?, defaultValue: @autoclosure () throws -> T) rethrows -> T

and does a "auto closure" on the second argument (in order to get a short-circuiting behaviour). So

self.value = dict["bar"] as? String ?? _defaultValue

is transformed by the compiler to

self.value = dict["bar"] as? String ?? { self._defaultValue }()

and here the compiler complains because self is captured before being fully initialised. (The error messages are slightly different between Swift 2 and Swift 3).

Two workarounds come to my mind (perhaps someone has a better one): You can assign the property to a local variable first:

init(dict: NSDictionary){
    let defValue = _defaultValue
    self.value = dict["bar"] as? String! ?? defValue
}

Or you can make it a static property of the class:

class Foo {
    static let _defaultValue = "N/A"
    let value: String

    init(dict: NSDictionary) {
        self.value = dict["bar"] as? String ?? Foo._defaultValue
    }
}
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download