Concrete fulfillment of existential requirement

protocol P {}

struct P1: P {}

protocol R {
    var anyPs: [any P] { get }
}

struct R1: R { // Error: Type 'R1' does not conform to protocol 'R'
    let anyPs: [P1]
}

Is this intentional, oversighted or a known temporary limitation?

My understanding is that you can't change the types of properties required by protocols. If you remove the brackets and just make the properties typed any P and P1, it should fail the same way.

Oh I just realized that arrays cannot be coerced anyways ([P1] as [any P]) due to variance so yeah...
Nevermind.

Wait. It indeed can be coerced.

let _ = [P1()] as [any P] // ok

And the compiler can do also implicit coercion.

let p1s = [P1()]
let _: [any P] = p1s // ok

So I guess the question is relevant. Does the type requirement really need to be so strict in this case?

Without the brackets the question remains the same, even if with get only requirement you want to use associatedtype rather than existential in 99% of cases.

Can't help but note this topic would make a great name for a film.

3 Likes

Not saying it's an ideal solution, but you could always do something like this:

protocol P {}

struct P1: P {}

protocol R {
    var anyPs: [any P] { get }
}

struct R1: R {
    let concretePs: [P1]
    var anyPs: [any P] { concretePs as [any P] }
}

This gets you both the benefits of the protocol conformance and you can still interact with the struct's stored property using concrete types.

It's intentional, the type of a witness must match the protocol requirement exactly. Perhaps you want to use an associated type instead?

protocol R {
    associatedtype MyP: P
    var anyPs: [MyP] { get }
}

Ok thank you!

No I meant to allow conformers to have heterogenous array.

In that case conformers must declare the type of their array as any P also, sorry.

2 Likes