Reputation: 10194
I would like to have a class with static initialization method:
class A {
required init() {
}
// this one works
class func f0() -> Self {
return self.init()
}
// this one works as well
class func f1() -> Self {
let create = { self.init() } // no error, inferred closure type is '() -> Self'
return create()
}
}
Unfortunately, Swift 3 compiler is unable to infer type for any closure more complex than { self.init() }
. For example:
class func f2() -> Self {
let create = {
// error: unable to infer complex closure return type; add explicit type to disambiguate
let a = self.init()
return a
}
return create()
}
Any attempt to specify closure type, variable type explicitly or cast from A
to Self
leads to an error:
class func f3() -> Self {
let create = { () -> Self in // error: 'Self' is only available in a protocol or as the result of a method in a class;
let a = self.init()
return a
}
return create()
}
class func f4() -> Self {
let create = {
let a: Self = self.init() // error: 'Self' is only available in a protocol or as the result of a method in a class;
return a
}
return create()
}
class func f5() -> Self {
let create = { () -> A in
let a = self.init()
return a
}
return create() as! Self // error: cannot convert return expression of type 'A' to return type 'Self'
}
The solution is to avoid closures using Self
.
This seems like a very unfortunate limitation of the compiler. Is there a reason behind it? Is this issue likely to be fixed in future Swift versions?
Upvotes: 4
Views: 143
Reputation: 299355
The fundamental problem is that Swift needs to determine the type of Self at compile time, but you want to determine it at runtime. It has been somewhat expanded to allow class functions to return Self if everything can be resolved at compile time, but Swift can't always prove that your types must be correct in some of these cases (sometimes because it doesn't know how yet, and sometimes because it's actually possible to be wrong).
As an example, consider a subclass that doesn't override a Self-returning method. How can it return the subclass? What if it passes Self to something else? How can you statically type check that at compile time given future subclasses the compiler doesn't even know about? (Including subclasses that can be added at runtime and across module boundaries.)
I expect this to get a little better, but Swift discourages this kind of complex inheritance (these are non-final classes, so you must consider all the possible subclasses) and prefers protocols long-term, so I wouldn't expect this to be fully possible in Swift 4 or 5.
Upvotes: 2