Swift返回不透明类型遵循Sequence协议,如何解决泛型推断问题?
如何让Swift的LinkedList用不透明类型实现Sequence协议且正常使用API?
问题场景
我有一个Swift包实现了LinkedList类,想要让它遵循Sequence协议。最初用不透明类型返回迭代器的实现如下:
extension LinkedList: Sequence { public func makeIterator() -> some IteratorProtocol { LinkedListIterator(head: head) } }
但调用map等Sequence特性时会触发编译错误:
func test_list_has_expected_elements() { let list = LinkedList(2, 4, 2, 3) XCTAssertEqual(list.map{ $0 }, [2, 4, 2, 3]) //Cannot convert value of type 'Int' to expected element type 'Array<(some IteratorProtocol).Element>.ArrayLiteralElement' (aka '(some IteratorProtocol).Element') }
如果改成返回具体的LinkedListIterator<T>类型,代码能正常编译,但必须将LinkedListIterator设为public,这不符合隐藏内部实现的需求:
extension LinkedList: Sequence { public func makeIterator() -> LinkedListIterator<T> { LinkedListIterator(head: head) } }
完整代码参考
public class LinkedList<T> { var head: Node<T>? init(_ elements : T...) { if let first = elements.first { var last = Node(data: first) head = last elements.dropFirst(1).forEach { element in let next = Node(data: element) last.next = next last = next } } } public func append(data: T) { let newNode = Node(data: data) if let last { last.next = newNode } else { head = newNode } } public var count: UInt { var runner = head var count: UInt = 0 while let node = runner { count += 1 runner = node.next } return count } private var last: Node<T>? { if var runner = head { while let next = runner.next { runner = next } return runner } return nil } } @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) extension LinkedList: Sequence { /* public func makeIterator() -> LinkedListIterator<T> { LinkedListIterator(head: head) } */ public func makeIterator() -> some IteratorProtocol { LinkedListIterator(head: head) } } public struct LinkedListIterator<T>: IteratorProtocol { private var currentNode: Node<T>? init(head: Node<T>? = nil) { self.currentNode = head } public mutating func next() -> T? { if let node = currentNode { currentNode = node.next return node.data } else { return nil } } }
Swift版本:swift-driver version: 1.62.15 Apple Swift version 5.7.1 (swiftlang-5.7.1.135.3 clang-1400.0.29.51)
解决方案
问题根源是Swift无法自动推断出不透明类型some IteratorProtocol的关联类型Element为T,需要显式约束关联类型,同时隐藏迭代器的具体实现:
- 修改
makeIterator的返回类型,显式指定关联类型Element = T:
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) extension LinkedList: Sequence { public func makeIterator() -> some IteratorProtocol<Element = T> { LinkedListIterator(head: head) } }
- 将
LinkedListIterator的访问级别改为默认的internal(去掉public修饰符),隐藏内部实现:
struct LinkedListIterator<T>: IteratorProtocol { private var currentNode: Node<T>? init(head: Node<T>? = nil) { self.currentNode = head } mutating func next() -> T? { if let node = currentNode { currentNode = node.next return node.data } else { return nil } } }
这样修改后,既通过不透明类型隐藏了迭代器的具体实现,又能让Swift正确推断Sequence的元素类型,正常使用map、filter等所有Sequence API。
内容的提问来源于stack exchange,提问作者rojarand
相关产品推荐
相关产品推荐

