Protocol Doesn't Conform to Itself

Protocol doesn't conform to itself?

EDIT: Eighteen more months of working w/ Swift, another major release (that provides a new diagnostic), and a comment from @AyBayBay makes me want to rewrite this answer. The new diagnostic is:

"Using 'P' as a concrete type conforming to protocol 'P' is not supported."

That actually makes this whole thing a lot clearer. This extension:

extension Array where Element : P {

doesn't apply when Element == P since P is not considered a concrete conformance of P. (The "put it in a box" solution below is still the most general solution.)


Old Answer:

It's yet another case of metatypes. Swift really wants you to get to a concrete type for most non-trivial things. [P] isn't a concrete type (you can't allocate a block of memory of known size for P). (I don't think that's actually true; you can absolutely create something of size P because it's done via indirection.) I don't think there's any evidence that this is a case of "shouldn't" work. This looks very much like one of their "doesn't work yet" cases. (Unfortunately it's almost impossible to get Apple to confirm the difference between those cases.) The fact that Array<P> can be a variable type (where Array cannot) indicates that they've already done some work in this direction, but Swift metatypes have lots of sharp edges and unimplemented cases. I don't think you're going to get a better "why" answer than that. "Because the compiler doesn't allow it." (Unsatisfying, I know. My whole Swift life…)

The solution is almost always to put things in a box. We build a type-eraser.

protocol P { }
struct S: P { }

struct AnyPArray {
var array: [P]
init(_ array:[P]) { self.array = array }
}

extension AnyPArray {
func test<T>() -> [T] {
return []
}
}

let arr = AnyPArray([S()])
let result: [S] = arr.test()

When Swift allows you to do this directly (which I do expect eventually), it will likely just be by creating this box for you automatically. Recursive enums had exactly this history. You had to box them and it was incredibly annoying and restricting, and then finally the compiler added indirect to do the same thing more automatically.

some Protocol causes type to not conform to protocol

The reason why it doesn't compile is because some Publisher declares an opaque type, but the protocol requires that the type must be "see-through".

some Publisher is "opaque" in the sense that callers cannot see exactly what type the property actually is, and can only know that it conforms to Publisher. This directly contradicts with the protocol requirement that P.Output has to be Int. To check P.Output is Int, you have to "see through" some Publisher, but you can't.

Since the compiler can't check the publisher's Output, it can't check whether your type really conforms to the protocol or not. Therefore it chooses the "safe route" concludes that your type does not conform to the protocol.

I think you should use the AnyPublisher type eraser:

var publisher: AnyPublisher<Int, Never> {
Just(1).eraseToAnyPublisher()
}

SwiftUI's View protocol does not have this problem because it does not require Body to be "see-through". It just requires that Body is a conformer of View, which some View, by definition, is.

Why sub-protocol doesn't fulfill conformance for its parent?

B describes a type that conforms to some set of rules. It is not itself a type that conforms to those rules. B does not conform to B, let alone anything it requires additional conformance to. protocol B:A says "anything that conforms to B also must conform to A." However, B does not conform to B, and so does not conform to A.

This has been answered many times on SO, but to repeat the "why," it comes down most simply to this example:

protocol A { 
init()
}

func f(type: A.Type) {
let a = type.init()
}

f(type: A.self)

If A itself conforms to A, then this should be legal. But what init should f call? In the presence of init and static requirements, it's not possible for protocols to conform to themselves.

While the specific covariance you want in this case is possible in principle, Swift doesn't have the ability to distinguish between legal and illegal covariance, and disallows all of it. For example, consider the small variation from immutable to mutable:

protocol C {
var property: A { get set }
}

In this case, it is definitely impossible for Bar to conform (it would have to provide a setter for property that accepted any A, even though the getter would return a subtype of A). Swift is not able to distinguish all the cases where it is possible (generally when everything is immutable and there are no init or static requirements) from the cases where it isn't. There's been some discussion of allowing these kinds of cases where it's technically possible, but the concern is that very small changes to the protocol could then break your entire type structure, sometimes for non-obvious reasons. Instead, at least for now, type requirements are generally invariant.


A typical way to address this overall issue is to just provide accessors for the types you want. For example:

protocol A { }
protocol B: A { }

protocol C {
var aProperty: A { get }
}

struct Foo: C {
let aProperty: A
}

struct Bar: C {
var aProperty: A { return bProperty }
let bProperty: B
}

This provides greater flexibility (you can make bProperty a var if you like), and makes your code more explicit.

Conditional Conformance: Using 'T' as a concrete type conformance to 'T' is not supported

Change the extension to Element is rather than Element conforms to

extension Array where Element == Social {
func speak() {
forEach { $0.speak() }
}
}

The constraint Array : Social is irrelevant.



Related Topics



Leave a reply



Submit