Func protocol returns Self

I have a P protocol that returns a copy of an object:

protocol P { func copy() -> Self } 

and a class C that implements P:

 class C : P { func copy() -> Self { return C() } } 

However, if I return my value as Self I get the following error:

Cannot convert return expression of type 'C' to return type 'Self'

I also tried returning C

 class C : P { func copy() -> C { return C() } } 

This led to the following error:

The 'copy ()' method in the indefinite class 'C' must return Self to conform to the protocol 'P'

Nothing works except when I prefix class C final .:

 final class C : P { func copy() -> C { return C() } } 

However, if I want to subclass C, then nothing will work. Is there any way around this?

+60
swift protocols swift-protocols subclassing
Sep 03 '14 at 13:02
source share
8 answers

The problem is that you promise that the compiler will not be able to prove that you will save.

So, you made this promise: the copy() call will return its own fully initialized type.

But then you implemented copy() as follows:

 func copy() -> Self { return C() } 

Now I am a subclass that does not override copy() . And I return C , not a fully initialized Self (which I promised). So nothing good. What about:

 func copy() -> Self { return Self() } 

Well, that doesn't compile, but even if that were the case, it would be nice. A subclass may not have a trivial constructor, so D() may not even be legal. (Although see below.)

OK, how about:

 func copy() -> C { return C() } 

Yes, but that does not return Self . It returns C You still do not keep your promise.

"But ObjC can do it!" Well, sort of. Mostly because it doesn't matter if you keep your promise, like Swift does. If you do not implement copyWithZone: in a subclass, you can completely not initialize your object. The compiler will not even warn you that you did this.

"But most of everything in ObjC can be translated into Swift, and ObjC has NSCopying ." Yes, and here is how it is defined:

 func copy() -> AnyObject! 

So you can do the same (no reason for here!):

 protocol Copyable { func copy() -> AnyObject } 

It says, "I promise nothing that you will return." You can also say:

 protocol Copyable { func copy() -> Copyable } 

This is a promise you can make.

But we can think a little about C ++ and remember that there is a promise we can make. We can promise that we and all our subclasses will implement certain types of initializers, and Swift will provide this (and therefore can prove that we are telling the truth):

 protocol Copyable { init(copy: Self) } class C : Copyable { required init(copy: C) { // Perform your copying here. } } 

Here's how you should make copies.

We can take this step further, but it uses dynamicType , and I have not tested it extensively to make sure that this is always what we want, but it should be correct:

 protocol Copyable { func copy() -> Self init(copy: Self) } class C : Copyable { func copy() -> Self { return self.dynamicType(copy: self) } required init(copy: C) { // Perform your copying here. } } 

Here we promise that there is an initializer that executes copies for us, and then at runtime we can determine which one to call by providing us with the syntax of the method you were looking for.

+130
Sep 03 '14 at 13:29
source share
— -

With Swift 2, we can use the protocol for this extension.

 protocol Copyable { init(copy:Self) } extension Copyable { func copy() -> Self { return Self.init(copy: self) } } 
+22
Oct. 16 '15 at 13:32
source share

Actually there is a trick that makes it easy to return Self when required by the protocol ( gist ):

 /// Cast the argument to the infered function return type. func autocast<T>(some: Any) -> T? { return some as? T } protocol Foo { static func foo() -> Self } class Vehicle: Foo { class func foo() -> Self { return autocast(Vehicle())! } } class Tractor: Vehicle { override class func foo() -> Self { return autocast(Tractor())! } } func typeName(some: Any) -> String { return (some is Any.Type) ? "\(some)" : "\(some.dynamicType)" } let vehicle = Vehicle.foo() let tractor = Tractor.foo() print(typeName(vehicle)) // Vehicle print(typeName(tractor)) // Tractor 
+10
Jan 16 '16 at 21:46
source share

There is another way to do what you want, which involves using a Swift-related type. Here is a simple example:

 public protocol Creatable { associatedtype ObjectType = Self static func create() -> ObjectType } class MyClass { // Your class stuff here } extension MyClass: Creatable { // Define the protocol function to return class type static func create() -> MyClass { // Create an instance of your class however you want return MyClass() } } let obj = MyClass.create() 
+7
Aug 10 '16 at 23:03
source share

By following Rob's recommendations, this can be made more general with related types . I modified this example a bit to demonstrate the benefits of this approach.

 protocol Copyable, NSCopying { typealias Prototype init(copy: Prototype) init(deepCopy: Prototype) } class C : Copyable { typealias Prototype = C // <-- requires adding this line to classes required init(copy: Prototype) { // Perform your copying here. } required init(deepCopy: Prototype) { // Perform your deep copying here. } @objc func copyWithZone(zone: NSZone) -> AnyObject { return Prototype(copy: self) } } 
+2
Aug 31 '15 at 16:53
source share

I had a similar problem, and I came up with something that might be useful, so I would share it for future reference, because this is one of the first places I found when looking for a solution.

As stated above, the problem is the ambiguity of the return type for the copy () function. This can be illustrated very clearly by separating the functions copy () → C and copy () → P:

So, assuming you define the protocol and class as follows:

 protocol P { func copy() -> P } class C:P { func doCopy() -> C { return C() } func copy() -> C { return doCopy() } func copy() -> P { return doCopy() } } 

Compiles and produces the expected results when the return type is explicit. Each time the compiler has to decide what the return type should be (by itself), it will detect the situation is ambiguous and fail for all concrete classes implementing the P protocol.

For example:

 var aC:C = C() // aC is of type C var aP:P = aC // aP is of type P (contains an instance of C) var bC:C // this to test assignment to a C type variable var bP:P // " " " P " " bC = aC.copy() // OK copy()->C is used bP = aC.copy() // Ambiguous. // compiler could use either functions bP = (aC as P).copy() // but this resolves the ambiguity. bC = aP.copy() // Fails, obvious type incompatibility bP = aP.copy() // OK copy()->P is used 

In conclusion, this will work in situations where you either do not use the base class copy () function, or you always have an explicit type context.

I found that using the same function name as a specific class created for cumbersome code all over the world, I ended up using a different name for the copy () function of the protocol.

The end result is more like:

 protocol P { func copyAsP() -> P } class C:P { func copy() -> C { // there usually is a lot more code around here... return C() } func copyAsP() -> P { return copy() } } 

Of course, my context and functions are completely different, but in the spirit of the question, I tried to stay as close as possible to the given example.

+1
Aug 23 '15 at 8:51
source share

Just throw your hat in the ring. We needed a protocol that returned an optional type on which the protocol was applied. We also wanted the override to explicitly return a type, not just Self.

Instead, ā€œIā€ is used as the return type, instead, you define a related type that you set to ā€œIā€, and then use this associated type.

Here's the old way using Self ...

 protocol Mappable{ static func map() -> Self? } // Generated from Fix-it extension SomeSpecificClass : Mappable{ static func map() -> Self? { ... } } 

Here's a new way to use a bound type. Note that the return type is now explicitly, not "I".

 protocol Mappable{ associatedtype ExplicitSelf = Self static func map() -> ExplicitSelf? } // Generated from Fix-it extension SomeSpecificClass : Mappable{ static func map() -> SomeSpecificClass? { ... } } 
0
Nov 29 '17 at 19:26
source share

To add answers using the associatedtype method, I suggest moving the instance creation to the standard implementation of the protocol extension. Thus, the corresponding classes will not have to implement it, thereby saving us from code duplication:

 protocol Initializable { init() } protocol Creatable: Initializable { associatedtype Object: Initializable = Self static func newInstance() -> Object } extension Creatable { static func newInstance() -> Object { return Object() } } class MyClass: Creatable { required init() {} } class MyOtherClass: Creatable { required init() {} } // Any class (struct, etc.) conforming to Creatable // can create new instances without having to implement newInstance() let instance1 = MyClass.newInstance() let instance2 = MyOtherClass.newInstance() 
0
May 9 '18 at 15:38
source share



All Articles