我正在学习来自其他语言的Swift和功能强大的类型系统,我想知道它们是否反过来使一个协议有条件地符合另一个协议。
让我们举个例子:我定义了一个ShowableAsInt
协议,该协议允许为符合该协议的任何类型获取一个Int
表示形式。
protocol ShowableAsInt {
func intRepr() -> Int
}
extension Int : ShowableAsInt {
func intRepr() -> Int {
return self
}
}
extension String : ShowableAsInt {
func intRepr() -> Int {
return self.count
}
}
func show(_ s: ShowableAsInt) {
print(s.intRepr())
}
show("abc") // "3"
show(42) // "42"
现在,我定义了一个Container
协议,该协议只包装了一个元素。
protocol Container {
associatedtype T
var element: T {
get
}
}
struct StringContainer : Container {
typealias T = String
let element: String
}
struct IntContainer : Container {
typealias T = Int
let element: Int
}
因为Container
是一个简单的包装器,所以只要包装的类型可以显示为Int
,那么容器也可以显示为Int
。我试图表达这一点,但失败了:
// Doesn't compile: can't extend a protocol to conform to another one?
extension Container : ShowableAsInt where T : ShowableAsInt {
func intRepr() -> Int {
return element.intRepr()
}
}
// I would like these to compile
show(IntContainer(42)) // "42"
show(StringContainer("abc")) // "3"
我知道可以在class
和struct
上表达这种条件一致性。有什么办法可以对协议做同样的事情吗?如果没有,是否有任何限制的理由?
答案 0 :(得分:2)
不允许这样做的原因here:
此协议扩展会将Equatable元素的任何Collection都变为Equatable,这是可以充分利用的强大功能。为协议扩展引入条件一致性会加剧重叠一致性的问题,因为说上述协议扩展的存在并不合理,这意味着没有任何符合Collection的类型可以声明自己符合Equatable ,有条件的或其他方式。
另请参阅this question。
如果您不想每次都编写intRepr
的重复实现,则可以执行以下操作:
struct StringContainer : ShowableAsIntContainer {
typealias T = String
let element: String
}
struct IntContainer : ShowableAsIntContainer {
typealias T = Int
let element: Int
}
extension Container where T : ShowableAsInt {
func intRepr() -> Int {
return element.intRepr()
}
}
typealias ShowableAsIntContainer = ShowableAsInt & Container