特定于枚举成员的通用约束

Tim*_*len 5 generics enums type-constraints swift

我有一个关联类型的协议:

protocol MyProtocol {
    associatedtype Q
}
Run Code Online (Sandbox Code Playgroud)

现在我想要一个枚举

enum MyEnum<Q> {
    case zero
    case one(MyProtocol)
    case two(MyProtocol, MyProtocol)
}
Run Code Online (Sandbox Code Playgroud)

其中每个关联的值都Q作为其关联类型。这不起作用:

enum MyEnum<Q> {
    case zero
    case one<P: MyProtocol where P.Q == Q>(P)
    case two<P1: MyProtocol, P2: MyProtocol where P1.Q == Q, P2.Q == Q>(P1, P2)
}
Run Code Online (Sandbox Code Playgroud)

显然,单个枚举成员不能有自己的通用约束。

我唯一能想到的就是将这些约束移动到枚举声明中,但这会固定关联的类型。为了证明为什么这不是我想要的,这就是我希望能够做的:

struct StructA: MyProtocol {
    typealias Q = Int
}

struct StructB: MyProtocol {
    typealias Q = Int
}

var enumValue = MyEnum.one(StructA())
enumValue = .two(StructB(), StructA())
enumValue = .two(StructA(), StructB())
Run Code Online (Sandbox Code Playgroud)

有没有办法绕过这个限制?

Rob*_*ier 3

键入擦除。答案始终是类型擦除。

您需要的是 AnyProtocol 类型:

struct AnyProtocol<Element>: MyProtocol {
    typealias Q = Element
    // and the rest of the type-erasure forwarding, based on actual protocol
}
Run Code Online (Sandbox Code Playgroud)

现在您可以创建一个使用它们的枚举

enum MyEnum<Q> {
    case zero
    case one(AnyProtocol<Q>)
    case two(AnyProtocol<Q>, AnyProtocol<Q>)
}
Run Code Online (Sandbox Code Playgroud)

有关如何构建类型橡皮擦的更深入讨论,请参阅对 AnySequence 的一点尊重

Swift 无法将 PAT(具有关联类型的协议)作为实际类型甚至抽象类型进行讨论。它们只能是限制。为了将其用作抽象类型,您必须将其提炼为类型橡皮擦。幸运的是,这是相当机械的,并且在大多数情况下并不困难。它是如此机械,最终编译器将有望为您完成这项工作。但必须有人来建造这个盒子,今天就是你。