【问题标题】:Why can't my Swift protocol extension wrap an existing function of the same type?为什么我的 Swift 协议扩展不能包装相同类型的现有函数?
【发布时间】:2017-04-23 23:57:43
【问题描述】:

我正在尝试为集合创建一个“安全”的下标运算符——它忽略超出集合可用索引的范围部分。

期望的行为是在所有情况下都返回一个 Slice;当下标范围和集合范围没有重叠时,应该返回空数组。

这似乎是对this answer 中提出的技术的简单扩展。 documentation of the collection subscript operator is very straightforward

subscript(bounds: Range<Self.Index>) -> Slice<Self> { get }

但是当我在包装函数中采用这些相同的类型时,我得到以下信息:

复制/粘贴版本:

extension Collection where Indices.Iterator.Element == Index {
    subscript(safe bounds: Range<Self.Index>) -> Slice<Self> {
        let empty = Slice(base: self, bounds: (startIndex..<startIndex))
        guard bounds.lowerBound < endIndex else { return empty }
        guard bounds.upperBound >= startIndex else { return empty }

        let lo = Swift.max(startIndex, bounds.lowerBound)
        let hi = Swift.min(endIndex, bounds.upperBound)
        return self[lo..<hi]
    }
}

为什么我不能以这种方式下标集合?为什么编译器确认我使用了正确类型的 Range&lt;Self.Index&gt;(在文档中指定)但仍然认为这是一个错误?

【问题讨论】:

  • 请注意,您的 where Indices.Iterator.Element == Index 约束未在您的自定义下标中使用。您可能需要考虑将其移动到 Collection 的无约束扩展中(假设您有其他方法使用当前方法中的约束)。
  • 更简单地说,Hamish 的回答是:self[lo..&lt;hi]Self.SubSequence,所以你不能在预期 Slice&lt;Self&gt; 的地方返回它。当然,错误消息(通常如此)非常具有误导性。
  • @Hamish 我认为我的扩展中的其他功能需要该约束(the one in this answer),但即便如此——根据我在那里的评论——我不明白它的构造
  • @Ian 有趣的是,我在this answer 中建议了该约束,您链接到的答案提到了该约束。您可以在该答案中看到Indices 的定义,并且您可以看到它没有承诺其元素的类型(因此编译器无法知道它们是否为Equatable,因此您是否可以在.indices上使用contains(_:)方法)。
  • 但是,一旦在关联类型上允许使用 where 子句,我希望 stdlib 团队在 Indices 中添加一个,将其 Iterator.Element 限制为 Index 类型(因为这就是它是索引的集合),这将允许您取消扩展约束。在那之前,我在第一条评论中的建议是对范围下标使用Collection 的一个无约束扩展,然后对Collection 进行另一个扩展,这次使用where Indices.Iterator.Element == Index 作为单个索引下标。

标签: swift swift-protocols


【解决方案1】:

The ranged subscript requirement of Collection 返回一个SubSequence(关联类型):

subscript(bounds: Range<Self.Index>) -> Self.SubSequence { get }

subscript that you refer to:

subscript(bounds: Range<Self.Index>) -> Slice<Self> { get }

只是该下标的默认实现,其中SubSequenceSlice&lt;Self&gt;。任何符合Collection 的类型都可以以不同的方式实现此要求——它们不必将SubSequence 定义为Slice&lt;Self&gt;

因此,您只需更改扩展名以反映您使用的下标返回 SubSequence

extension Collection {
    subscript(safe bounds: Range<Index>) -> SubSequence {

        // as the method returns a SubSequence,
        // use the regular subscript to get an empty SubSequence
        let empty = self[startIndex..<startIndex]

        guard bounds.lowerBound < endIndex else { return empty }
        guard bounds.upperBound >= startIndex else { return empty }

        let lo = Swift.max(startIndex, bounds.lowerBound)
        let hi = Swift.min(endIndex, bounds.upperBound)

        return self[lo..<hi]
    }
}

【讨论】:

  • 因此,如果我理解正确,编译器错误反映了我正在尝试的 subscript 操作不会发生返回 Slice&lt;Self&gt; 的想法?
  • @Ian 是的,编译器错误告诉您(以一种相当糟糕的方式)它无法在返回Slice&lt;Self&gt;Collection 协议中找到subscript 要求,作为要求在协议中返回SubSequence,而不是Slice&lt;Self&gt;
  • 虽然下标要求有一个默认实现,它返回一个Slice&lt;Self&gt;,但编译器无法保证它存在于任意集合中(你当然可以限制扩展名,以便@ 987654344@ Slice&lt;Self&gt;,这将允许您当前的代码工作,但我看不出引入该约束的任何实际理由。
  • 看起来真正的问题是我过度滚动了 collection reference page 并且没有意识到我正在查看默认实现定义而不是协议定义。
猜你喜欢
  • 2019-10-21
  • 1970-01-01
  • 2015-10-13
  • 1970-01-01
  • 1970-01-01
  • 2021-09-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多