Swift - 序列 - 为什么我不需要为元素和迭代器添加类型别名?

Swift - Sequence - Why I'm not required to add typealiases for Element and Iterator?

我正在学习Swift,不明白为什么我可以声明

class MultiSet<T: Hashable> : Swift.Sequence {
    var data = [T]()
    
    func makeIterator() -> Array<T>.Iterator {
        return data.makeIterator()
    }
}

那行得通。但是,如果我复制 Sequence 声明并写入

class MultiSet<T: Hashable> : MySequence {
    var data = [T]()
    
    func makeIterator() -> Array<T>.Iterator {
        return data.makeIterator()
    }
}

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

我收到需要显式编写协议存根的错误:

    typealias Element = <#type#>
    typealias Iterator = <#type#>

它是如何自动推断出来的?

谢谢!

这是因为您在 associatedtype 声明中添加的约束只是一个约束,并没有真正解析类型。

为了能够解析 associatedtype,您需要在协议(或实现)中对 associatedtype 进行某种引用。

在您的示例中,您的实施仅“解决”了 Iterator associatedtype:

public protocol MySequence {
    func makeIterator() -> Self.Iterator
}

class MultiSet<T: Hashable> : Swift.Sequence {
    func makeIterator() -> Array<T>.Iterator {
        return data.makeIterator()
    }
}

因为你在实现中用Array<T>.Iterator“替换”了Iterator,Swift明白了Iterator应该如何解决。但是那个时候Element还是没有解决。

Note: Xcode does ask for both Element and Iterator type aliases in the implementation, but Element is the only one actually missing. If you add

typealias Element = T

In MultiSet it fixes the error.

如果您希望 Swift 能够解析 Element,您将需要相同类型的“参考”,无论是在 MultiSet 的实现中还是直接在您的协议中一个扩展。

例如:

public protocol MySequence {
    associatedtype Element where Self.Element == Self.Iterator.Element
    associatedtype Iterator : IteratorProtocol
    func makeIterator() -> Iterator

    func foobar() -> Element? // added a reference to Element
}

extension MySequence {
    func foobar() -> Iterator.Element? {
        nil
    }
}

struct MultiSet<T>: MySequence {
    var data = [T]()

    func makeIterator() -> Array<T>.Iterator {
        return data.makeIterator()
    }
}

这将允许 Swift 将 Element 解析为 Iterator.Element

Note: You can define foobar inside MultiSet without a default implementation in an extension. I used this example because it is how it's done inside Sequence.

如果您看一下 Sequence 实现 (Source code),您会注意到 _customContainsEquatableElement 函数(引用 Element),默认实现 (将 Element 解析为 Iterator.Element)

public protocol Sequence {
  func _customContainsEquatableElement(
    _ element: Element
  ) -> Bool?
}

// [...]

extension Sequence {
  public func _customContainsEquatableElement(
    _ element: Iterator.Element
  ) -> Bool? {
    return nil
  }
}

这就是为什么在实施 Sequence 时您不必显式解析 Element,它会隐式解析为 Iterator.Element