问题描述
在阅读 Apple 的关于 opaque types 的 Swift 编程语言指南时,有一段我不明白。该指南讨论了不透明类型和协议之间的区别,并指出您不能嵌套返回协议类型的调用。他们使用这个代码片段,其中 Shape 是一个协议:
func protoFlip<T: Shape>(_ shape: T) -> Shape {
if shape is Square {
return shape
}
return FlippedShape(shape: shape)
}
然后声明:
这种方法的另一个问题是形状变换不会嵌套。翻转三角形的结果是一个 Shape 类型的值,protoFlip(:) 函数接受一个符合 Shape 协议的某种类型的参数。但是,协议类型的值不符合该协议; protoFlip(:) 返回的值不符合 Shape。这意味着像 protoFlip(protoFlip(smallTriange)) 这样应用多个变换的代码是无效的,因为翻转的形状不是 protoFlip(_:) 的有效参数。
然而,我写了这段代码:
import Foundation
protocol P {
associatedtype AT
}
struct C: P {
typealias AT = Int
}
func f<T: P>(_ t: T) -> T {
t
}
func g() {
f(f(C()))
}
g()
这会编译并运行……而且似乎让我可以嵌套这些调用。
我误解了什么?文档想表达什么?
解决方法
你写的:
func f<T: P>(_ t: T) -> T
这需要并返回相同的类型。
那不是问题。问题是例子:
func protoFlip<T: Shape>(_ shape: T) -> Shape
这需要一个 T 并返回一个存在的 Shape。
这相当于:
func f<T: P>(_ t: T) -> P
(取一个 T 并返回一个存在的 P。)
如果你写了那个,你会发现你遇到了所描述的问题。您不能将 P 存在性传递到 f()
中,因为协议存在性不符合它们在 Swift 中的协议。 (这是由于静态方法和初始值设定项可以创建各种极端情况。而不是处理极端情况,今天的协议存在性只是不符合他们的协议。这可能会在未来发生变化的情况下可以允许,但目前不允许。)
不透明类型允许你写:
func f<T: P>(_ t: T) -> some P
返回一个符合 P 的具体(但不透明)类型,而不是返回 P 存在类型。系统将其跟踪为“当由 T 参数化时由 f 返回的类型”。它不等同于任何其他 some P
,但它是具体的,在编译时已知,并且可以传入 f()
。