【问题标题】:How does Swift infer Sequence requirements when Self implements IteratorProtocol?Self 实现 IteratorProtocol 时,Swift 如何推断 Sequence 需求?
【发布时间】:2021-03-05 20:27:14
【问题描述】:

我正在阅读序列文档,并在他们使用的示例中(见下文)推断出序列的要求。我的问题是它是如何推断的。我理解推理在更简单的情况下是如何工作的,但在这个例子中,我无法理解 Swift 是如何推理事物的。

我看到 Sequence 的 makeIterator 方法有一个默认实现,但我不明白这里是如何推断返回值的。

示例

struct Countdown: Sequence, IteratorProtocol {

    var count: Int

    mutating func next() -> Int? {
        if count == 0 {
            return nil
        } else {
            defer { count -= 1 }
            return count
        }
    }
}

上述类型的参考

IteratorProtocol {

   func next() -> Self.Element?

   associatedtype Iterator

   associatedtype Element
}
Sequence {

   func makeIterator() -> Self.Iterator

   associatedtype Iterator

   associatedtype Element

}

【问题讨论】:

  • 你似乎在Sequence的声明中出现了复制粘贴错误。
  • @Sweeper 谢谢我刚刚编辑

标签: swift types protocols sequence type-inference


【解决方案1】:

让我们从IteratorProtocol.next的实现开始。编译器看到这个实现:

mutating func next() -> Int? {
    if count == 0 {
        return nil
    } else {
        defer { count -= 1 }
        return count
    }
}

并注意到它返回一个Int?。好吧,IteratorProtocol.next 应该返回一个Self.Element?,所以它推断出IteratorProtocol.Element == Int。现在Coundown 满足IteratorProtocol

注意SequenceIteratorProtocol 共享 关联类型Element。一旦 Swift 找出了 IteratorProtcol.Element 的见证人,就好像你在 Countdown 中声明了一个新的类型别名 Element,而 Sequence 恰好要求 Countdown.Element 存在。

之后,编译器推断Iterator == Self。这样makeIterator 的默认实现就可用了。然而,编译器如何推断这一点却是一个谜,因为只有这些信息,通常无法推断出类型,这可以通过创建自己的序列和迭代器协议来证明。

protocol MyIterator {
    associatedtype Element
    
    mutating func next() -> Element?
}

protocol MySequence {
    associatedtype Element where Element == Iterator.Element
    associatedtype Iterator : MyIterator
    
    func makeIterator() -> Iterator
}

extension MySequence where Self == Self.Iterator {
    func makeIterator() -> Iterator {
        return self
    }
}

struct Countdown: MySequence, MyIterator { // doesn't compile
    var count: Int

    mutating func next() -> Int? {
        if count == 0 {
            return nil
        } else {
            defer { count -= 1 }
            return count
        }
    }
}

在查看source code 之后,我怀疑可能有一些编译器魔法正在发生,尤其是这里:

// Provides a default associated type witness for Iterator when the
// Self type is both a Sequence and an Iterator.
extension Sequence where Self: IteratorProtocol {
  // @_implements(Sequence, Iterator)
  public typealias _Default_Iterator = Self
} 

这似乎为Iterator 设置了一个“首选”类型来推断为。它似乎在说“当Iterator 不能被推断为任何东西时,试试Self”。我在其他任何地方都找不到_Default_Iterator,这就是为什么我断定它是编译器的魔法。这样做的全部目的是让您通过仅符合IteratorProtocol 并实现next 来符合Sequence,就像documentation 所说的那样。

现在有了Iterator == Self,我们也满足了Element的约束:

associatedtype Element where Self.Element == Self.Iterator.Element

因此我们已经证明Countdown 符合Sequence

【讨论】:

  • 我可能已经想通了。如果我们实现 Sequence,我们将设置: typealias Iterator as Self 因为 CountDown 是一个 Iterator 并且 Swift 可以推断 Self 是它自己的迭代器。虽然,您对正在发生的编译器魔术有一个很好的观点。正如您还指出的那样,也许您可​​以改写您的答案以突出编译器推断它的点,因为它知道 self 是一个迭代器。我将其标记为正确
  • @3366784 我不确定你的意思,但我已经尽了最大努力并进行了编辑。随意提出修改建议:)
猜你喜欢
  • 2017-04-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-29
  • 2017-12-21
  • 2021-02-21
  • 1970-01-01
  • 2018-11-01
  • 1970-01-01
相关资源
最近更新 更多