符合条件的协议规范

时间:2019-06-03 17:04:00

标签: swift generics swift-protocols

我正在学习来自其他语言的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"

我知道可以在classstruct上表达这种条件一致性。有什么办法可以对协议做同样的事情吗?如果没有,是否有任何限制的理由?

1 个答案:

答案 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