I have a protocol P that returns a copy of the object:
protocol P {
func copy() -> Self
}
and a class C that implements P:
class C : P {
func copy() -> Self {
return C()
}
}
However, whether I put the return 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()
}
}
That resulted in the following error:
Method 'copy()' in non-final class 'C' must return
Self
to conform to protocol 'P'
Nothing works except for the case where I prefix class C
with final
ie do:
final class C : P {
func copy() -> C {
return C()
}
}
However if I want to subclass C then nothing would work. Is there any way around this?
I had a similar problem and came up with something that may be useful so I though i'd share it for future reference because this is one of the first places I found when searching 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 copy() -> C and copy() -> P functions:
So, assuming you define the protocol and class as follows:
This compiles and produces the expected results when the type of the return value is explicit. Any time the compiler has to decide what the return type should be (on its own), it will find the situation ambiguous and fail for all concrete classes that implement the P protocol.
For example:
In conclusion, this would work in situations where you're either, not using the base class's copy() function or you always have explicit type context.
I found that using the same function name as the concrete class made for unwieldy code everywhere, so I ended up using a different name for the protocol's copy() function.
The end result is more like:
Of course my context and functions are completely different but in spirit of the question, I tried to stay as close to the example given as possible.
The problem is that you're making a promise that the compiler can't prove you'll keep.
So you created this promise: Calling
copy()
will return its own type, fully initialized.But then you implemented
copy()
this way:Now I'm a subclass that doesn't override
copy()
. And I return aC
, not a fully-initializedSelf
(which I promised). So that's no good. How about:Well, that won't compile, but even if it did, it'd be no good. The subclass may have no trivial constructor, so
D()
might not even be legal. (Though see below.)OK, well how about:
Yes, but that doesn't return
Self
. It returnsC
. You're still not keeping your promise."But ObjC can do it!" Well, sort of. Mostly because it doesn't care if you keep your promise the way Swift does. If you fail to implement
copyWithZone:
in the subclass, you may fail to fully initialize your object. The compiler won't even warn you that you've done that."But most everything in ObjC can be translated to Swift, and ObjC has
NSCopying
." Yes it does, and here's how it's defined:So you can do the same (there's no reason for the ! here):
That says "I'm not promising anything about what you get back." You could also say:
That's a promise you can make.
But we can think about C++ for a little while and remember that there's a promise we can make. We can promise that we and all our subclasses will implement specific kinds of initializers, and Swift will enforce that (and so can prove we're telling the truth):
And that is how you should perform copies.
We can take this one step further, but it uses
dynamicType
, and I haven't tested it extensively to make sure that is always what we want, but it should be correct:Here we promise that there is an initializer that performs copies for us, and then we can at runtime determine which one to call, giving us the method syntax you were looking for.
There is another way to do what you want that involves taking advantage of Swift's associated type. Here's a simple example:
With Swift 2, we can use protocol extensions for this.
Actually, there is a trick that allows to easily return
Self
when required by a protocol (gist):To add to the answers with the
associatedtype
way, I suggest to move the creating of the instance to a default implementation of the protocol extension. In that way the conforming classes won't have to implement it, thus sparing us from code duplication: