swiftinterfaceswift2protocol-extension

Method in non-final class must return `Self` to conform to protocol


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.

Update

As of Swift 4 (possibly 3), the above code works as-is.


Solution

  • 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.