为Collection一致性提供默认实现可以防止额外的下标要求

时间:2017-09-03 22:04:49

标签: swift swift-protocols

我有一个协议,它本身符合Swift的Collection协议,并且需要额外的下标(Key) -> Value?返回与给定键相关联的值,只要它存在(非常类似于Swift的Dictionary 1}}确实)。

protocol SearchTree: Collection {
    subscript(key: Int) -> String? { get }
}

struct ConformingTree: SearchTree {
    // Implementation ...
}

现在,我想用所有Collection要求的默认实现扩展它,加上我的附加下标(我想实现细节是无关紧要的,这就是我删除它们的原因)。

protocol SearchTree: Collection {
    subscript(key: Int) -> String? { get }
}

extension SearchTree {
    // MARK: Conformance to Sequence
    func makeIterator() -> AnyIterator<(key: Int, value: String)> { /* ... */ }

    // MARK: Conformance to Collection
    var startIndex: Int { /* ... */ }
    var endIndex: Int { /* ... */ }
    func index(after i: Int) -> Int { /* ... */ }
    subscript(key: Int) -> (key: Int, value: String) { /* ... */ }

    // MARK: Conformance to SearchTree
    subscript(key: Int) -> String? { /* ... */ }
}

struct ConformingTree: SearchTree {
    // Removing previous implementations ...
}

不幸的是,此代码将失败,Swift抱怨ConformingTree不符合Collection,除非我保留了符合类型中至少一个下标的实现。

struct ConformingTree: SearchTree {
    subscript(key: Int) -> String? { /* ... */ }
}

我原本以为Swift无法推断扩展中正确下标的类型。但这似乎不太可能,因为它最终可以确定哪个实现对应于哪个协议要求,如果我将它们推入符合类型。据我了解,makeIterator()的返回类型应强制带有签名(Int) -> (Key, String)的下标,以满足Collection的要求。

有谁知道我在这里缺少什么?

1 个答案:

答案 0 :(得分:1)

两个问题:

  1. 您在SearchTree协议中声明的下标需要{ get }之后。

  2. Collection需要一个返回其Element的下标。你有两个下标,其中一个返回String?,其中一个返回(key: Int, value: String),但这些都不是编译器需要的Element;因此类型不符合Collection。如果您在协议或扩展中定义Element,则应编译。

  3. 在协议中:

    associatedtype Element = (key: Int, value: String)

    或:

    associatedtype Element = String?

    或者在扩展程序中:

    typealias Element = (key: Int, value: String)

    或:

    typealias Element = String?

    编辑:

    以上适用于Swift 4;但是,对于Swift 3,除了_Element之外,还需要定义Element。将代码复制并粘贴到项目中,以下协议声明会导致在Swift 3中编译所有内容:

    protocol SearchTree: Collection {
        associatedtype Element = (key: Int, value: String)
        associatedtype _Element = (key: Int, value: String)
    
        subscript(key: Int) -> String? { get }
    }