为什么我们不能转换为具有关联类型的协议类型,但使用泛型实现相同的效果? [英] Why can we not cast to protocol types with associated types but achieve the same effect using generics?

查看:24
本文介绍了为什么我们不能转换为具有关联类型的协议类型,但使用泛型实现相同的效果?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑这个代码:

扩展集合{func foo() ->整数{如果 self.first 是 Collection {return (self.first as! Collection).underestimatedCount//错误}别的 {返回 self.underestimatedCount}}}

我们得到了可怕的、显然令人困惑的:

<块引用>

协议Collection"只能用作通用约束,因为它具有 Self 或关联的类型要求.

然而,这愉快地编译:

func foo(_ c: C) ->Int where C.Iterator.Element: Collection {如果让第一个 = c.first {返回 first.underestimatedCount//*} 别的 {返回 c.underestimatedCount}}

为什么?!

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

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

解决方案

使用存在容器"表示协议类型的值(请参阅 关于他们的 WWDC 精彩演讲;或 在 Youtube 上),它由一个固定大小的值缓冲区组成,用于存储值(如果值大小超过这个,它将堆分配),一个指向协议见证表的指针,按顺序查找方法实现和指向值见证表的指针,以管理值的生命周期.

非专业泛型使用几乎相同的格式(我在此问答中更深入地在此问答中)– 当它们被调用时,指向协议和值见证表的指针被传递给函数,并且值本身使用值缓冲区本地存储在函数内部,该缓冲区将为大于该缓冲区的值进行堆分配.

因此,由于这些实现方式的完全相似,我们可以得出这样的结论,即不能就具有关联类型的协议或泛型之外的 Self 约束进行讨论只是一个语言的当前限制.没有真正的技术原因为什么它不可能,只是还没有实现(还).

这是 上的泛型宣言的摘录广义存在主义",讨论了这在实践中如何运作:

<块引用>

存在类型的限制来自一个实现限制,但允许协议类型的值是合理的即使协议具有 Self 约束或关联类型.为了例如,再次考虑 IteratorProtocol 以及如何将其用作存在主义:

protocol IteratorProtocol {关联类型元素变异 func next() ->元素?}让它:IteratorProtocol = ...it.next()//如果这被允许,它可以返回一个Any?",即包装实际元素的存在

另外,想要约束关联是合理的存在的类型,例如,一个 Sequence,其元素类型是String" 可以通过将 where 子句放入protocol<...>Any<...>(根据Renaming protocol<...> to 任何<...>"):

let strings: Any= ["a", "b", "c"]

开头的 . 表示我们在谈论动态类型,即,符合 Sequence 协议的 Self 类型.我们没有理由不能支持任意的 where 子句Any<...>.

并且由于能够将值键入为具有关联类型的协议,这只是允许将类型转换为该给定类型的一小步,从而允许编译您的第一个扩展之类的东西.

Consider this code:

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

We get the dreaded and apparently widely puzzling:

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

However, this happily compiles:

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

Why?!

In particular, the compiler does not know in * how the associated types of (the type of) first have been realized; it only gets the promise that they have been (because any object of type Collection has to realize them). This same guarantee is there in the first example! So why does the compiler complain about one but not the other?

My question is: at line *, what does the compiler know that it does not in line ERROR?

解决方案

Protocol-typed values are represented using an 'existential container' (see this great WWDC talk on them; or on Youtube), which consists of a value-buffer of fixed size in order to store the value (if the value size exceeds this, it'll heap allocate), a pointer to the protocol witness table in order to lookup method implementations and a pointer to the value witness table in order to manage the lifetime of the value.

Unspecialised generics use pretty much the same format (I go into this in slightly more depth in this Q&A) – when they're called, pointers to the protocol and value witness tables are passed to the function, and the value itself is stored locally inside the function using a value-buffer, which will heap allocate for values larger than that buffer.

Therefore, because of the sheer similarity in how these are implemented, we can draw the conclusion that not being able to talk in terms of protocols with associated types or Self constraints outside of generics is just a current limitation of the language. There's no real technical reason why it's not possible, it just hasn't been implemented (yet).

Here's an excerpt from the Generics Manifesto on "Generalized existentials", which discusses how this could work in practice:

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

And from being able to type a value as a protocol with an associated type, it's but a short step to allow for type-casting to that given type, and thus allow something like your first extension to compile.

这篇关于为什么我们不能转换为具有关联类型的协议类型,但使用泛型实现相同的效果?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆