When implementing a static protocol function returning Self
in a protocol extension, an error appears at the implementation of the function in the extension (minimal simplified scenario shown without context):
import Foundation
protocol P {
static func f() -> Self
static func g() -> Self
}
extension P {
static func f() -> Self { // Method 'f()' in non-final class 'NSData' must return `Self` to conform to protocol 'P'
return g()
}
}
extension NSData: P {
static func g() -> Self {
return self.init()
}
}
Replacing Self
with P
on the line the error occurs causes the compiler to segfault (sig 11) (which seems a valid way of conveying a type mismatch error).
Changing the declaration of f()
to return P
, as well as replacing Self
with P
on the error line, results in successful compilation, however loses type precision (and requires force downcasting at each call site, plus documenting the Self
requirement in detail).
Are there any other workarounds for this issue that do not lose the generic return type?
EDIT: Further details to compensate for lack of context: P
is a public protocol that will be exposed by a library, for various types to conform to (and override g()
), so overriding f()
in NSData
is not an option. It is also preferable to not have to change f()
to something other than a protocol extension, as it is used by the library internally in a number of places. Given these two options, changing the return type of f()
to P
is a better alternative.
As of Swift 4 (possibly 3), the above code works as-is.
In Swift 3 or 4:
import Foundation
protocol P {
static func f() -> Self
static func g() -> Self
}
extension P {
static func f() -> Self {
return g()
}
}
extension Data: P {
static func g() -> Data {
return self.init()
}
}
Or you can replace your class with a final subclass:
import Foundation
protocol P {
static func f() -> Self
static func g() -> Self
}
extension P {
static func f() -> Self {
return g()
}
}
import Foundation
final class MyData: NSData {}
extension MyData: P {
static func g() -> Self {
return self.init()
}
}
If NSData is one of those class clusters that can't easily be subclassed (you'll see a stacktrace with __CFRequireConcreteImplementation
), you may have to create a final class wrapper for a real NSData instead of using a subclass.