21

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.

Greg
  • 9,068
  • 6
  • 49
  • 91
  • 2
    "causes the compiler to segfault (sig 11) (which seems a valid way of conveying a type mismatch error)." Please make sure you report that to Apple. – JeremyP Dec 15 '15 at 14:59

6 Answers6

8

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.

Jano
  • 62,815
  • 21
  • 164
  • 192
  • I solved my situation making my class `final` as you said. Thanks! – Wooseong Kim Oct 27 '16 at 02:57
  • Are you sure you've tested it? It doesn't work in Swift 3 though. It still complains `Method 'g()' in non-final class 'Data' must return `Self` to conform to protocol 'P'.` – Ozgur Vatansever Aug 08 '17 at 14:02
  • @OzgurVatansever I don’t remember but probably I did. I don’t have Swift 3 anymore but I edited to include complete examples that compile in Swift 4. – Jano Aug 08 '17 at 15:19
  • Thanks for your answer @Jano. Making the class final works in my case but I want to understand why is that so? I mean why final class resolves this whereas non-final class gives this error? – Ram Patra Mar 13 '22 at 12:43
1

this works for me ....

protocol P {
    static func foo()->Self
}

class C {
    required init() {}
}
extension C: P {
    static func foo() -> Self {
        return self.init()
    }
}

let c = C()
let c2 = C.foo()
print(c.dynamicType, c2.dynamicType) // C C

ok, i see you note, so i made an update

protocol P {
    static func foo()->Self
    static func bar()->Self
}
extension P {
    static func foo() -> Self { // Method 'f()' in non-final class 'NSData' must return `Self` to conform to protocol 'P'
        return bar()
    }
}
// the class C must be final ...
final class C {
}
// otherwise the compiler can not decide the type of Self in 
extension C: P {
    static func bar() -> Self {
        return self.init()
    }
}

let c = C()
let c2 = C.foo()
print(c.dynamicType, c2.dynamicType) // C C

with NSData, if you would like to make the same, the trouble is to declare NSData as final.

user3441734
  • 16,722
  • 2
  • 40
  • 59
  • How does this help me? I mentioned that the example I provided is simplified — I can't just remove the function `g` in the real thing. – Greg Dec 15 '15 at 15:00
1

You'll need to override f() in your NSData extension.

The basic problem is (I think) that the compiler doesn't know what Self is when it compiles f in the protocol extension and I think it assumes it must be the exact type of the class it is applying it too. With NSData, that might not be the case because you might have a subclass of it.

JeremyP
  • 84,577
  • 15
  • 123
  • 161
  • Sorry if I was unclear; unfortunately overriding `f` in `NSData` isn't an option, as that's just one example of many classes that'll be conforming, some out of my control. I've updated the question with additional details. Seems like I'll have to wait for Swift 3.0 for this to become possible without workarounds... – Greg Dec 15 '15 at 15:42
1

As of Swift 2.1, I was able to avoid the given error by only having structs (or 'final' classes) conform to the protocol. Currently, you can restrict protocol implementation to reference-types ("classes-only"), but I have not seen a similar restriction for value-types.

In this particular case though, I'd say a delegate pattern would suit. If appropriate, you could move a default implementation of f into a protocol extension, and have subclass implementations override a delegate property.

protocol P {
    static var delegate : (() -> Self)?;
}

extension P {
    static func f() -> Self {
        // Use delegate if available, or use default implementation
    }
}

extension NSData : P {
    static var delegate : (() -> NSData)? = subImplementation;

    func subImplementation() -> NSData {
        // Some NSData-specific implementation
    }
}
MandisaW
  • 971
  • 9
  • 21
1

You can also get around it by using an associated type.

protocol P {
    associatedtype Entity
    static func f() -> Entity
    static func g() -> Entity
}

extension P {
    static func f() -> Entity {
        return g()
    }
}

extension Data: P {
    static func g() -> Data {
        return self.init()
    }
}

You don't need to specify the Entity in your implementation as the compiler will infer it from your return type.

Trenskow
  • 3,783
  • 1
  • 29
  • 35
0

I've got the same problem, did you figure it out?

Here is another way I came out to deal with particular situation. Instead of using protocol which required a static function returning Self, maybe you can consider defining a Initializer-required protocol.

Like this:

protocol SomeProtocol {
    init(someParameter: Int)
}

Dont forget to mark the initializer implementation with the required keyword.

class SomeClass: SomeProtocol {
    required init(someParameter: Int) {

    }
}

Hope this could help.

Frain
  • 1
  • 1