通用协议类型参数与直接协议类型的差异 [英] Differences generic protocol type parameter vs direct protocol type

查看:63
本文介绍了通用协议类型参数与直接协议类型的差异的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是我的游乐场代码:

protocol A {
    init(someInt: Int)
}

func direct(a: A) {
    // Doesn't work
   let _ = A.init(someInt: 1)
}

func indirect<T: A>(a: T) {
    // Works
    let _ = T.init(someInt: 1)
}

struct B: A {
    init(someInt: Int) {

    }
}

let a: A = B(someInt: 0)

// Works
direct(a: a)

// Doesn't work
indirect(a: a)

使用参数 a 调用方法 indirect 时会出现编译时错误.所以我理解<T:A>的意思是某种符合A的类型.我的变量a的类型是A并且协议不符合他们自己所以好吧,我理解编译时错误.

It gives a compile time error when calling method indirect with argument a. So I understand <T: A> means some type that conforms to A. The type of my variable a is A and protocols do not conform to themselfs so ok, I understand the compile time error.

同样适用于方法 direct 中的编译时错误.我明白了,需要插入一个具体的符合类型.

The same applies for the compile time error inside method direct. I understand it, a concrete conforming type needs to inserted.

尝试访问 direct 中的 static 属性时也会出现编译时间.

A compile time also arrises when trying to access a static property in direct.

我想知道.定义的 2 种方法是否存在更多差异?我知道我可以从 indirect 调用初始值设定项和静态属性,我可以直接在 direct 中插入类型 A 并且分别,我不能做什么另一个可以.但是我错过了什么吗?

I am wondering. Are there more differences in the 2 methods that are defined? I understand that I can call initializers and static properties from indirect and I can insert type A directly in direct and respectively, I can not do what the other can do. But is there something I missed?

推荐答案

关键的混淆是 Swift 有两个拼写相同的概念,因此常常有歧义.一个是struct T:A{},意思是T符合协议A",另一个是var a:A,意思是变量a的类型是A的existential."

The key confusion is that Swift has two concepts that are spelled the same, and so are often ambiguous. One of the is struct T: A {}, which means "T conforms to the protocol A," and the other is var a: A, which means "the type of variable a is the existential of A."

遵守协议不会改变类型.T 仍然是 T.正好符合一些规则.

Conforming to a protocol does not change a type. T is still T. It just happens to conform to some rules.

一个存在的"是一个编译器生成的盒子,它包装了一个协议.这是必要的,因为符合协议的类型可能具有不同的大小和不同的内存布局.存在是一个盒子,它为任何符合协议的东西在内存中提供一致的布局.存在和协议是相关的,但不是一回事.

An "existential" is a compiler-generated box the wraps up a protocol. It's necessary because types that conform to a protocol could be different sizes and different memory layouts. The existential is a box that gives anything that conforms to protocol a consistent layout in memory. Existentials and protocols are related, but not the same thing.

因为existential 是一个运行时盒子,可以保存任何类型,所以涉及一些间接性,这可能会影响性能并阻止某些优化.

Because an existential is a run-time box that might hold any type, there is some indirection involved, and that can introduce a performance impact and prevents certain optimizations.

另一个常见的混淆是理解类型参数的含义.在函数定义中:

Another common confusion is understanding what a type parameter means. In a function definition:

func f<T>(param: T) { ... }

这定义了一系列函数 f<T>(),它们是在编译时根据您作为类型参数传递的内容创建的.例如,当你这样调用这个函数时:

This defines a family of functions f<T>() which are created at compile time based on what you pass as the type parameter. For example, when you call this function this way:

f(param: 1)

在编译时创建了一个名为 f() 的新函数.这是一个与 f()f<[Double]>() 完全不同的函数.每一个都是自己的函数,原则上是f()中所有代码的完整副本.(在实践中,优化器非常聪明,可能会消除一些复制.还有一些与跨越模块边界的事情相关的其他微妙之处.但这是考虑正在发生的事情的一种相当不错的方式.)

a new function is created at compile time called f<Int>(). That is a completely different function than f<String>(), or f<[Double]>(). Each one is its own function, and in principle is a complete copy of all the code in f(). (In practice, the optimizer is pretty smart and may eliminate some of that copying. And there are some other subtleties related to things that cross module boundaries. But this is a pretty decent way to think about what is going on.)

由于为每个传递的类型创建了泛型函数的专用版本,理论上它们可以更优化,因为函数的每个版本将只处理一种类型.权衡是他们可以添加代码膨胀.不要假设泛型比协议快".泛型可能比协议更快是有原因的,但您必须实际查看代码生成和配置文件才能了解任何特定情况.

Since specialized versions of generic functions are created for each type that is passed, they can in theory be more optimized, since each version of the function will handle exactly one type. The trade-off is that they can add code-bloat. Do not assume "generics are faster than protocols." There are reasons that generics may be faster than protocols, but you have to actually look at the code generation and profile to know in any particular case.

因此,浏览您的示例:

func direct(a: A) {
    // Doesn't work
   let _ = A.init(someInt: 1)
}

协议(A)只是类型必须遵守的一组规则.你不能构建一些符合这些规则的未知事物".将分配多少字节的内存?它将为规则提供哪些实现?

A protocol (A) is just a set of rules that types must conform to. You can't construct "some unknown thing that conforms to those rules." How many bytes of memory would be allocated? What implementations would it provide to the rules?

func indirect<T: A>(a: T) {
    // Works
    let _ = T.init(someInt: 1)
}

为了调用这个函数,你必须传递一个类型参数T,并且该类型必须符合A.当你使用特定类型调用它时,编译器会创建一个新的indirect副本code> 专门设计用于与您传递的 T 一起使用.由于我们知道 T 有一个正确的 init,我们知道编译器将能够在需要时编写此代码.但是indirect 只是编写函数的模式.它本身不是一个函数;除非你给它一个 T 来使用.

In order to call this function, you must pass a type parameter, T, and that type must conform to A. When you call it with a specific type, the compiler will create a new copy of indirect that is specifically designed to work with the T you pass. Since we know that T has a proper init, we know the compiler will be able to write this code when it comes time to do so. But indirect is just a pattern for writing functions. It's not a function itself; not until you give it a T to work with.

let a: A = B(someInt: 0)

// Works
direct(a: a)

a 是 B 的存在包装器.direct() 需要一个存在包装器,因此您可以传递它.

a is an existential wrapper around B. direct() expects an existential wrapper, so you can pass it.

// Doesn't work
indirect(a: a)

a 是 B 的存在包装器.存在包装器不符合协议.它们需要符合协议的东西才能创建它们(这就是为什么它们被称为存在物";您创建一个的事实证明了这样的值确实存在).但它们本身并不符合协议.如果他们这样做了,那么你可以做一些你在 direct() 中尝试做的事情,然后说在不知道里面究竟是什么的情况下创建一个存在性包装器的新实例".没有办法做到这一点.存在包装器没有自己的方法实现.

a is an existential wrapper around B. Existential wrappers do not conform to protocols. They require things that conform to protocols in order to create them (that's why they're called "existentials;" the fact that you created one proves that such a value actually exists). But they don't, themselves, conform to protocols. If they did, then you could do things like what you've tried to do in direct() and say "make a new instance of an existential wrapper without knowing exactly what's inside it." And there's no way to do that. Existential wrappers don't have their own method implementations.

在某些情况下,存在的可以符合它自己的协议.只要没有initstatic要求,其实原则上没有问题.但是 Swift 目前无法处理.因为它不能用于 init/static,所以 Swift 目前在所有情况下都禁止它.

There are cases where an existential could conform to its own protocol. As long as there are no init or static requirements, there actually isn't a problem in principle. But Swift can't currently handle that. Because it can't work for init/static, Swift currently forbids it in all cases.

这篇关于通用协议类型参数与直接协议类型的差异的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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