为什么我们不能转换为具有关联类型的协议类型,而是使用泛型达到相同的效果?

Why can we not cast to protocol types with associated types but achieve the same effect using generics?

考虑这段代码:

extension Collection {
    func foo() -> Int {
        if self.first is Collection {
            return (self.first as! Collection).underestimatedCount // ERROR
        }
        else {
            return self.underestimatedCount
        }
    }
}

我们得到了可怕且显然令人费解的问题:

protocol 'Collection' can only be used as a generic constraint because it has Self or associated type requirements.

然而,这愉快地编译:

func foo<C: Collection>(_ c: C) -> Int where C.Iterator.Element: Collection {
    if let first = c.first {
        return first.underestimatedCount // *
    } else {
        return c.underestimatedCount
    }
}

为什么?!

特别是,编译器知道*first的关联类型(的类型)是如何实现的;它只会得到它们曾经的承诺(因为任何Collection类型的对象都来实现它们)。第一个例子中也有同样的保证!那么,为什么编译器抱怨一个而不抱怨另一个呢?

我的问题是:在第 * 行,编译器知道什么它不在第 ERROR 行?

Protocol-typed 值使用 'existential container'(参见 this great WWDC talk on them; or on Youtube)表示,它由固定大小的 value-buffer 组成,以便存储值(如果value size exceeds this, it'll heap allocate), a pointer to the protocol witness table or order to lookup method implementations and a pointer to the value witness table 为了管理值的生命周期.

非专用泛型使用几乎相同的格式(我对此进行了更深入的探讨 )——当它们被调用时,指向协议和值见证的指针 table 被传递到函数,并且值本身使用 value-buffer 在函数内部本地存储,它将堆分配大于该缓冲区的值。

因此,由于它们的实现方式非常相似,我们可以得出结论,不能根据具有关联类型的协议或泛型之外的 Self 约束进行讨论只是当前的一个问题语言的限制。没有真正的技术原因为什么它不可能,它只是还没有实现。

以下是关于“Generalized existentials”的泛型宣言的摘录,其中讨论了这在实践中如何运作:

The restrictions on existential types came from an implementation limitation, but it is reasonable to allow a value of protocol type even when the protocol has Self constraints or associated types. For example, consider IteratorProtocol again and how it could be used as an existential:

protocol IteratorProtocol {
  associatedtype Element
  mutating func next() -> Element?
}

let it: IteratorProtocol = ...
it.next()   // if this is permitted, it could return an "Any?", i.e., the existential that wraps the actual element

Additionally, it is reasonable to want to constrain the associated types of an existential, e.g., "a Sequence whose element type is String" could be expressed by putting a where clause into protocol<...> or Any<...> (per "Renaming protocol<...> to Any<...>"):

let strings: Any<Sequence where .Iterator.Element == String> = ["a", "b", "c"]

The leading . indicates that we're talking about the dynamic type, i.e., the Self type that's conforming to the Sequence protocol. There's no reason why we cannot support arbitrary where clauses within the Any<...>.

从能够将值键入为具有关联类型的协议开始,这只是允许 type-casting 到该给定类型的一小步,从而允许编译类似于您的第一个扩展的东西。