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

Providing a default implementation for Collection conformance prevents additional subscript requirements

我有一个协议,它本身符合 Swift 的 Collection 协议,并且需要一个额外的下标 (Key) -> Value? return 与给定的值关联键,仅当它存在时(很像 Swift 的 Dictionary)。

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() 的 return 类型应该强制带有签名 (Int) -> (Key, String) 的下标无论如何满足 Collection 的要求。

有人知道我在这里错过了什么吗?

两个问题:

  1. 您在 SearchTree 协议中的下标声明需要在其后加上 { get }

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

在协议中:

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

或:

associatedtype Element = String?

或在扩展中:

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

或:

typealias Element = String?

编辑:

以上为Swift4;但是,对于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 }
}