简体   繁体   English

Swift枚举涉及依赖协议的多种泛型类型

[英]Swift enum multiple generic types involving depending protocols

protocol ProtocolA {
    func someFunc() -> Any
}

protocol ProtocolB: ProtocolA {
    var someVar: Any { get }
}

enum MyEnum<T: ProtocolA, U: ProtocolB> {
    case A(T)
    case B(U)
}

protocol DataObject {
    ...
}

extension DataObject where Self: ProtocolB {
    func doSomething() {
        let x = MyEnum.B(self)
        /// Compiler error:
        /// Cannot invoke 'B' with an argument list of type '(Self)'
    }
}

I don't understand why the above is giving me an error. 我不明白为什么上面给我一个错误。 Strangely, removing either of the two enum generic constraints, leaving the enum with a single constraint, solves the problem... 奇怪的是,删除两个枚举通用约束中的任何一个,使枚举只有一个约束,解决了问题......

Note that ProtocolB extends ProtocolA. 请注意,ProtocolB扩展了ProtocolA。 Is this not supported when used with generic constraints? 与通用约束一起使用时,是否不支持此功能?


UPDATE UPDATE

Changing MyEnum to this solves the problem: MyEnum更改MyEnum可以解决问题:

enum MyEnum {
    typealias T = ProtocolA
    typealias U = ProtocolB

    case A(T)
    case B(U)
}

However, I still don't quite get why... 但是,我仍然不明白为什么......

The error is a little misleading. 这个错误有点误导。 Let's simplify the problem to explore it. 让我们简化问题来探索它。

protocol ProtocolA {}

protocol ProtocolB: ProtocolA {}

enum MyEnum<T: ProtocolA, U: ProtocolB> {
    case A(T)
    case B(U)
}

struct AThing: ProtocolA {}

struct BThing: ProtocolB {}

let bThing = BThing()
let thing = MyEnum.B(bThing) // error: cannot invoke 'B' with an argument list of type '(BThing)'

So now we have the same problem without needing DataObject . 所以现在我们DataObject了同样的问题而不需要DataObject Why does this fail? 为什么这会失败?

MyEnum is generic. MyEnum是通用的。 That means that in order to create a concrete type, it must know the types of T and U . 这意味着为了创建具体类型,它必须知道TU的类型。 You provided constraints on those types (one conforms to ProtocolA and the other to ProtocolB ), but you didn't say specifically what types they are. 您提供了对这些类型的约束(一个符合ProtocolA ,另一个符合ProtocolB ),但您没有具体说明它们是什么类型。

When we get to this line: 当我们到达这一行时:

let thing = MyEnum.B(bThing)

What type is thing ? 什么类型的thing With type inference, we can work out what U is, but what is T ? 通过类型推断,我们可以弄清楚U是什么,但是什么是T

let thing: MyEnum<?, BThing> = MyEnum.B(bThing)

There's just not enough context here to work it out, so we have to tell the compiler explicitly: 这里没有足够的上下文来解决它,所以我们必须明确地告诉编译器:

let thing = MyEnum<AThing, BThing>.B(bThing)

So the full type of thing is MyEnum<AThing, BThing> , which is a different type than MyEnum<OtherAThing, BThing> . 所以完整类型的thingMyEnum<AThing, BThing> ,这是一种与MyEnum<OtherAThing, BThing>不同的类型。 (Exactly like [Int] is different type than [String] , which is why let xs = [] won't compile without an explicit type definition.) (正如[Int][String]类型不同,这就是为什么let xs = []在没有显式类型定义的情况下不会编译。)

Your second example that works is not generic, so there's no problem. 你的第二个例子不通用,所以没有问题。 It simplifies to: 它简化为:

enum MyEnum {
    case A(ProtocolA)
    case B(ProtocolB)
}

Bound typealiases just rename types, they don't create new types (like an unbound typealias would, or as Swift 2.2 calls it, associatedtype ). 绑定类型只是重命名类型,它们不会创建新类型(如未绑定的类型,或者Swift 2.2调用它, associatedtype )。 So we know that A takes any ProtocolA and B takes any ProtocolB , and all MyEnum instances have the same type. 所以我们知道A接受任何ProtocolAB接受任何ProtocolB ,并且所有MyEnum实例具有相同的类型。

The error is unfortunate because the compiler is confused. 错误是不幸的,因为编译器很困惑。 You'd get a clearer error if you simplified this down to the most basic case. 如果将此简化为最基本的情况,则会得到更明确的错误。 This fails for exactly the same reason as your example. 这失败的原因与您的示例完全相同。

enum MyEnum<T> {
    case A
}

let x = MyEnum.A // error: generic parameter 'T' could not be inferred

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM