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

时间:2017-01-17 11:19:39

标签: swift casting typechecking swift-protocols associated-types

考虑以下代码:

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

我们感到害怕,显然令人费解:

  

协议'Collection'只能用作通用约束,因为它具有Self或相关类型要求。

然而,这很愉快地编译:

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的相关类型({1}};它只获得它们已经存在的 promise (因为Collection 类型的任何对象都有来实现它们)。在第一个例子中也有同样的保证!那么为什么编译器会抱怨一个而不是另一个呢?

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

1 个答案:

答案 0 :(得分:3)

协议类型的值使用'存在容器'表示(请参阅this great WWDC talk;或on Youtube),它由固定大小的值缓冲区组成,以便存储值(如果值大小超过这个值,它将堆分配),指向协议见证表的指针,以便查找方法实现和指向值见证表的指针,以便管理值的生命周期。

非专用泛型使用几乎相同的格式(我稍稍深入in this Q&A) - 当它们被调用时,指向协议和值见证表的指针被传递给函数,值本身使用值缓冲区本地存储在函数内部,值缓冲区将为大于该缓冲区的值进行堆分配。

因此,由于这些实现方式的相似性,我们可以得出这样的结论:无法用相关类型的协议进行讨论,或者在泛型之外的Self约束仅仅是当前的限制。语言。没有真正的技术原因,为什么它不可能,它还没有实现(还)。

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

  

对存在类型的限制来自实施   限制,但允许协议类型的值是合理的   即使协议具有自我约束或相关类型。对于   例如,再次考虑IteratorProtocol以及如何将其用作   存在主义:

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
     

此外,想要约束相关联是合理的   存在性的类型,例如,元素类型为“a Sequence   String“可以通过将where子句放入其中来表达   protocol<...>Any<...>(每次“重命名protocol<...>Any<...>”):

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

前导.表示我们正在讨论动态类型,   即,符合Self协议的Sequence类型。   我们没有理由不支持其中的任意where条款   Any<...>

由于能够将值键入为具有关联类型的协议,因此只允许对该给定类型进行类型转换,这样就可以实现类似第一个扩展的编译。