Swift不透明类型与协议 - 文档暗示协议的函数不能嵌套。

5
阅读苹果的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()

这个可以编译并运行...并且似乎允许我嵌套这些调用。

我理解错了什么吗?文档想要表达什么意思?

1个回答

5
你写了这个:
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() 方法。

非常好 - 对为什么存在这个限制进行了非常棒的解释。谢谢 - Philip Pegden
2
Swift 演进:https://forums.swift.org/t/se-0309-unlock-existential-types-for-all-protocols/47515 - Sajjon
1
我能够毫无问题地编写protoFlip(protoFlip(smallTriangle)),并且它返回FlippedShape<FlippedShape<Triangle>>。我正在使用Apple Swift 5.7.2版本,是否有任何更改允许这样做? - CRDave
2
@CRDave 是的。请查看Sajjon提供的链接。“为所有协议解锁存在性。”具体请参阅类型擦除包装器部分。据我所知,书中的不透明类型部分目前正在根据编译器的最新更改进行重新制作。 - Rob Napier

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接