As an answer to your question:
But shouldn't be this code somehow not be compiled?
Well, at some point your code snippet worked without any issue (because -as you mentioned- the class A
init doesn't actually throws), so it could be compiled without any problem. To make it more clear, consider it as a similar case to the following one:
let myString: String? = nil
print(myString!) // crashes!
it will get compiled just fine! although we all know that it crashes when evaluating myString!
, i,e we do know it causes a run-time crash, but that doesn't mean that the compiler should prevent it because it could be valid at some point (for instance if we declare it as let myString: String? = "Hello"
); Similarly to your case, it could be valid at some point -as mentioned above-.
Usually, for such cases we -as developers- are the responsible to handle it based on what's the desired behavior(s).
Referring to this case, we might need to ask:
"How can we implement the instance
lazy variable to catch an error (with a do-catch
block)?"
Actually, this code won't compile:
class B {
lazy var instance:A = {
do {
let myA = try A()
return myA
} catch {
print(error)
}
}()
}
complaining that:
Missing return in a closure expected to return 'A'
because obviously reaching the catch block means that there is nothing to be returned. Also, as you mentioned even if you implemented it as
lazy var instance = A()
you will not get a compile-time error, however trying to use it with an actual throwing should leads to run time error:
let myB = B()
print(myB.instance) // crash!
What I would suggest for resolving this issue is to declare instance
as lazy optional variable:
class B {
lazy var instance:A? = {
do {
let myA = try A()
return myA
} catch {
print(error)
}
return nil
}()
}
At this point, if we assume that A
initializer always throws, trying to access it:
let myB = B()
print(myB.instance)
should log:
caught error
nil
without causing any crash. Otherwise, it should works fine, for instance:
let myB = B()
myB.instance?.doSomething() // works fine