Protocol doesn't conform to itself?

前端 未结 3 1820
陌清茗
陌清茗 2020-11-21 04:30

Why doesn\'t this Swift code compile?

protocol P { }
struct S: P { }

let arr:[P] = [ S() ]

extension Array where Element : P {
    func test() -&g         


        
3条回答
  •  陌清茗
    陌清茗 (楼主)
    2020-11-21 05:03

    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

    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] {
            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.

提交回复
热议问题