Универсальный тип соответствует последовательности с помощью непрозрачного типа: некоторый IteratorProtocol

#swift #generics #iterator #opaque-types

#swift #общие #итератор #непрозрачные типы

Вопрос:

 protocol TreeNode: AnyObject {
    associatedtype T
    var value: T { get set }
    var children: [Self] { get }
    init(_ value: T)
}

protocol Tree: Sequence {
    associatedtype Node: TreeNode
    var root: Node? { get set }
}

extension Tree {
    typealias T = Node.T
    
    func makeIterator() -> some IteratorProtocol {
        BFSIterator(startFrom: root)
    }
}
  

Это компилируется и выглядит очень многообещающе.
Но затем внезапно в строке модульных тестов let sum = tree.reduce(0, ) возникает ошибка компиляции:

Не удается преобразовать значение типа ‘(Int) -> Int’ в ожидаемый тип аргумента ‘(Int, (некоторый IteratorProtocol).Элемент) выдает -> Int’

Почему компилятор не может понять, что (some IteratorProtocol).Element это действительно Int так? И как этому помочь?

Обратите внимание, что если я сделаю «по-старому» (без непрозрачных типов): func makeIterator() -> BFSIterator { все компилируется и работает отлично.

Обновить:

 struct BFSIterator<Node: TreeNode>: IteratorProtocol {
    private var queue: Queue<Node> = []
    
    init(startFrom root: Node?) {
        root.map { queue.push($0) }
    }
    
    mutating func next() -> Node.T? {
        guard let current = queue.pop() else { return nil }
        queue.push(contentsOf: current.children)
        return current.value
    }
}
  

Комментарии:

1. Что такое BFSIterator ?

2. @NewDev struct BFSIterator<Узел: TreeNode>: IteratorProtocol { мутирующая функция next() -> Node.T? {

3. Пожалуйста, добавьте к вопросу… похоже, это важная часть

Ответ №1:

Это происходит потому, что в текущем Swift (5.2) нет способа указать связанный тип для непрозрачного возвращаемого значения. Таким образом, some IteratorProtocol компилятору недостаточно выяснить, какие значения должен next() возвращать метод.

Это ограничение языка вынуждает вас явно объявлять тип итератора, если вы хотите фактически использовать последовательность.