Why covariance does not apply when working with protocols in some situations

I used to believe I can always replace a protocol type with its conforming type, just like replacing a base class with a derived class.

However, I came across some weird behaviors when working with protocols.

Given:

protocol P { }

class C: P { }

In the case of conformance, the following code does not compile, the compiler says: " type 'Derived1' does not conform to protocol 'Base1"

protocol Base1 {

    func foo() -> P

}

class Derived1: Base1 {

    func foo() -> C { fatalError() }

}

In the case of inheritance, the following code does not compile neither, the compiler says: "method does not override any method from its superclass"

class Base2 {

    func foo() -> P { fatalError() }

}

class Derived2: Base2 {

    override func foo() -> C { fatalError() }

}

if I change P to be a base class, in the latter case everything will fine, however the former case still fails.

What confuses me more is the face that it is actually covariant in theory. As the following valid line shows:

let foo: () -> P = { () -> C in
  fatalError()
}

Can someone tell me, is my understanding about covariance corrent, or is it just an implementation limitation.

You are correct that C conforms to P. What you are missing is that P (the existential type) does not conform to P (the protocol).

This is not purely an implementation limitation, as there are reasons why some protocols cannot ever conform to themselves.

(As a special exception, the protocol Error conforms to itself by compiler magic; that protocol has no requirements.)

In your case, you probably want the protocol requirement to return a value of an associated type constrained to P. This will, however, prevent you from using that protocol as existential type.

1 Like

Oh my god, you saved my day! Thanks so much.

I will change my Base1 protocol to be a PAT and create a type-erased Box type to fulfill some of my tasks.

As for Base2, I think I should just change it to be a generic class.