Sergey Voitovich Sergey Voitovich - 4 months ago 11
Swift Question

Confusing `constant captured by a clojure before being initalized` error

I have the following swift class

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

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


Compilation fails with the 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
}
}


So if
_defaultValue
declared and initialized inside
init
call compiler doesn't complain and program works fine.

How to explain such strange thing?

Answer

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
    }
}