在设计 API 时,我在每个角落都会遇到这个问题:
Protocol can only be used as a generic constraint because it has Self or associatedType requirements
这意味着如果您计划单独使用协议(protocol)类型,您基本上不能在协议(protocol)级别使用 Self
或 associatedtype
—— 否则需要类型本身是通用的级联失控。
这很烦人(阅读:几乎不可能设计合理的 API,例如公开协议(protocol)但隐藏实现)。例如,你不能有一个简单的数据容器协议(protocol):
protocol DataContainer: Equatable {
var id: Int { get }
var content: Data { get }
}
问题是 Equatable
需要静态函数 ==
,当然,它是使用 Self
表示的。因此,您不能拥有像 [DataContainer]
这样的类型,或任何数量的其他自然事物。我首先遇到了用户组(数组)(可能是三种不同类型之一)。
“官方”推荐的“解决方案”是使用委托(delegate)/包装结构来消除类型变量,例如 AnyDataContainer
。这感觉像是一个相当愚蠢的变通方法,不是正确的 API 设计。
老实说,我不明白为什么类型系统有这个限制。原因是什么?为什么编译器不能为每个协议(protocol) P
隐式创建 AnyP
来绕过它?
最佳答案
这是我能想到的一个原因,实际上也不需要神秘的设置。基于问题中的示例,假设我们可以有这样的代码:
func contained(element: DataContainer, in list: [DataContainer]) -> Bool {
for elem in list {
if elem == element {
return true
}
}
return false
}
Swift 编译器想要静态分派(dispatch) ==
;但它不能!它没有可以选择的单一实现;每个数组元素都可能有自己的类型,因此 ==
的实现,而 element
可能有一个完全不同的类型。因此,编译器禁止使用手头的代码。
另外两个想法。
我认为编译器过于具有防御性。下面的代码可以很容易地编译,例如:
func contained(id: Int, in list: [DataContainer]) -> Bool { for elem in list { if elem.id == id { return true } } return false }
在这里,我们不需要知道数组元素的确切类型;他们每个人都有一个
Int
属性id
的 promise 就足够了!因此它可以允许使用像
[DataContainer]
这样的类型,只要所有访问的属性都可以静态解析。如果在何处分派(dispatch)和/或存在通用实现没有歧义,编译器可以允许
Self
引用。例如(伪代码):extension DataContainer { static func ==<T: DataCointainer, U: DataContainer>(lhs: T, rhs:U) -> Bool { guard T == U else { return false } return T.==(lhs, rhs as! T) } }
这样的默认实现可以在上述使用上下文中使用,即如果没有足够的类型信息可用于静态选择实现。
我不知道是否可以通过类似的想法解决通用协议(protocol)的所有问题,以及我们是否会在此过程中遇到进一步的限制。
关于swift - 为什么要限制带有 Self 或 associatedtype 的协议(protocol)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44389942/