First, it's important to explain why this is not perfectly valid code, and that it isn't clear at all that self.type
is not used before it is initialized. Consider the following extension of your code:
struct A {
init(_ f: (Int) -> Void) { f(1) }
}
class C {
let type: A
var num = 0 {
didSet { print(type) }
}
init() {
self.type = A({ (num: Int) -> Void in
self.num = num
})
}
}
If you walk through the logic, you'll note that self.type
is accessed via print
before it has been initialized. Swift can't currently prove that this won't happen, and so doesn't allow it. (A theoretical Swift compiler might prove that it wouldn't happen for some particular cases, but for most non-trivial code it would likely bump into the halting problem. In any case, the current Swift compiler isn't powerful enough to make this proof, and it's a non-trivial proof to make.)
One solution, though somewhat unsatisfying, is to use implicitly unwrapped optionals:
private(set) var type: A! = nil
Except for the declaration, every other part of the code is the same. You don't have to treat it as optional. In practice, this just turns off the "used before initialization" checks for this variable. It also unfortunately makes it settable inside of the current file, but does make it immutable to everyone else.
This is the technique I've most often used, though often I try to rework the system so that it doesn't require this kind of closure (not always possible, but I often rack my brain to try). It's not beautiful, but it is consistent and bounds the ugly.
Another technique that can work in some cases is laziness:
class C {
lazy var type: A = {
A({ (num: Int) -> Void in self.num = num })}()
var num = 0
init() {}
}
Sometimes that works, sometimes it doesn't. In your case it might. When it does work, it's pretty nice because it makes the property truly immutable, rather than just publicly immutable, and of course because it doesn't require !
.