How do you implement protocol methods that return a covariant self?

error: protocol "Protocol requirement" "instance" cannot be satisfied by an odd class ("class"), because it uses "I" in the position of a non-parametric, non-result of type

protocol Protocol { var instance: Self {get} } class Class: Protocol { var instance: Class {return Subclass()} } class Subclass: Class {} 

This is how I would like to express what I want in C #. (C #, as far as I know, is not able to ensure that the general parameter β€œI” is actually the β€œI” that we know from Swift, but it functions quite well, like documentation that will make me do the right thing).

 interface Protocol<Self> where Self: Protocol<Self> { Self instance {get;} } class Class: Protocol<Class> { public Class instance {get {return new Subclass();}} } class Subclass: Class {} 

... how it might look in a future version of Swift:

 protocol Protocol { typealias FinalSelf: Protocol where FinalSelf.FinalSelf == FinalSelf var instance: FinalSelf {get} } class Class: Protocol { var instance: Class {return Subclass()} } class Subclass: Class {} 

How do I emulate part of what is relevant to my problem:

 protocol Protocol: ProtocolInstance { static var instance: ProtocolInstance {get} } protocol ProtocolInstance {} class Class: Protocol { static var instance: ProtocolInstance {return Subclass()} } class Subclass: Class {} 

And here is what I consider to be the appropriate part of my code:

 protocol Protocol { static var 🎁: Self? {get} // an existing instance? static var πŸ₯: Self {get} // a new instance func instanceFunc() } extension Protocol { static func staticFunc() { (🎁 ?? πŸ₯).instanceFunc() } } 
+5
source share
2 answers

As the saying goes, you cannot do this, and for good reason. You cannot prove that you keep your promise. Consider this:

 class AnotherSubclass: Class {} let x = AnotherSubclass().instance 

So x should be AnotherSubclass according to your protocol ( Self ). But actually it will be Subclass , it is a completely different type. You cannot resolve this paradox if the class is final . This is not a limitation of Swift. This restriction will exist in any valid type system, since it admits a type conflict.

On the other hand, you can make instance return some consistent type in all subclasses (e.g. superclass). You do this with a related type:

 protocol Protocol { typealias InstanceType var instance: InstanceType {get} } class Class: Protocol { var instance: Class {return Subclass()} } class Subclass: Class {} class AnotherSubclass: Class {} let x = AnotherSubclass().instance 

Now x uniquely Class type. (This is also a random other subclass that looks weird, but this is what the code says.)

By the way, all this usually assumes that you use subclasses when you really should not. Composition and protocols probably solve this problem better in Swift. Ask yourself if there is any reason why Subclass should be a subclass of Class . Could it be an independent type that conforms to the same protocol? All problems disappear when you get rid of subclasses and focus on protocols.


I thought about it more, and there may be a way to get what you are looking for. Instead of saying that all subclasses implement instance , attach instance to the extension. You can still override this if you want to return something else.

 protocol Protocol { init() } class Class: Protocol { required init() {} var instance: Class { return Subclass() } } extension Protocol { var instance: Self { return self.dynamicType.init() } } class Subclass: Class {} 

This avoids the inheritance problem (you cannot create the same β€œ AnotherClass returning the wrong type” this way).

+6
source

This actually makes sense and works if you don't want to return Self for each subclass, like this:

 protocol Protocol : class { typealias Sub : Self var instance: Sub {get} } 

This means that your protocol defines a type that must be a subclass of itself. The following code will work:

 class Class: Protocol { var instance: Class {return Subclass()} } class Subclass: Class {} Class().instance // Returns SubClass() 

However, the above code does not compile with an error

 error: inheritance from non-protocol, non-class type '`Self`' 

which I consider to be a mistake, because Self declared as a class type. However, you can do it something like this:

 protocol Protocol : class { typealias Sub : Class var instance: Sub {get} } 

but then you don’t have much of the protocol itself, because only the class itself should correspond to it.

+2
source

Source: https://habr.com/ru/post/1233189/


All Articles