NonCopyableLinkedList.swift (5893B)
1 // 2 // NonCopyableLinkedList.swift 3 // damus 4 // 5 // Created by Daniel D’Aquino on 2025-07-04. 6 // 7 8 /// A linked list to help with iteration of non-copyable elements 9 /// 10 /// This is needed to provide an array-like abstraction or iterators since swift arrays or iterator protocols require the element to be "copyable" 11 struct NonCopyableLinkedList<T: ~Copyable>: ~Copyable { 12 private var head: Node<T>? = nil 13 private var tail: Node<T>? = nil 14 private(set) var count: Int = 0 15 16 /// Iterates over each item of the list, with enumeration support. 17 func forEachItem<Y>(_ borrowingFunction: ((_ index: Int, _ item: borrowing T) throws -> LoopCommand<Y>)) rethrows -> Y? { 18 var indexCounter = 0 19 20 var cursor: Node? = self.head 21 22 outerLoop: while let nextItem = cursor { 23 let loopIterationResult = try borrowingFunction(indexCounter, nextItem.value) 24 indexCounter += 1 25 cursor = nextItem.next 26 switch loopIterationResult { 27 case .loopBreak: 28 break outerLoop 29 case .loopContinue: 30 continue outerLoop 31 case .loopReturn(let result): 32 return result 33 } 34 } 35 36 return nil 37 } 38 39 /// Iterates over each item of the list in reverse, with enumeration support. 40 func forEachItemReversed<Y, E: Error>(_ borrowingFunction: ((_ index: Int, _ item: borrowing T) throws(E) -> LoopCommand<Y>)) throws(E) -> Y? { 41 var indexCounter = count - 1 42 var cursor: Node? = self.tail 43 44 outerLoop: while let nextItem = cursor { 45 let loopIterationResult = try borrowingFunction(indexCounter, nextItem.value) 46 indexCounter -= 1 47 cursor = nextItem.previous 48 switch loopIterationResult { 49 case .loopBreak: 50 break outerLoop 51 case .loopContinue: 52 continue outerLoop 53 case .loopReturn(let result): 54 return result 55 } 56 } 57 58 return nil 59 } 60 61 /// Iterates over each item of the list, with enumeration support, updating some value in each iteration and returning the final value at the end. 62 func reduce<Y>(initialResult: Y, _ borrowingFunction: ((_ index: Int, _ partialResult: Y, _ item: borrowing T) throws -> LoopCommand<Y>)) throws -> Y { 63 var indexCounter = 0 64 var currentResult = initialResult 65 66 var cursor: Node? = self.head 67 68 outerLoop: while let nextItem = cursor { 69 let loopIterationResult = try borrowingFunction(indexCounter, currentResult, nextItem.value) 70 indexCounter += 1 71 cursor = nextItem.next 72 switch loopIterationResult { 73 case .loopBreak: 74 break outerLoop 75 case .loopContinue: 76 continue outerLoop 77 case .loopReturn(let result): 78 currentResult = result 79 continue outerLoop 80 } 81 } 82 83 return currentResult 84 } 85 86 /// Uses a specific item of the list based on a provided index. 87 /// 88 /// O(N/2) worst case scenario 89 /// 90 /// Returns `nil` if nothing was found 91 func useItem<Y>(at index: Int, _ borrowingFunction: ((_ item: borrowing T) throws -> Y)) rethrows -> Y? { 92 if index < 0 || index >= self.count { 93 return nil 94 } 95 else if index < self.count / 2 { 96 return try self.forEachItem({ i, item in 97 if i == index { 98 return .loopReturn(try borrowingFunction(item)) 99 } 100 return .loopContinue 101 }) 102 } 103 else { 104 return try self.forEachItemReversed({ i, item in 105 if i == index { 106 return .loopReturn(try borrowingFunction(item)) 107 } 108 return .loopContinue 109 }) 110 } 111 } 112 113 /// Adds an item to the tail end list 114 mutating func add(item: consuming T) { 115 guard self.head != nil, let currentTail = self.tail else { 116 let firstNode = Node(value: item, next: nil, previous: nil) 117 self.head = firstNode 118 self.tail = firstNode 119 self.count = 1 120 return 121 } 122 let newTail = Node(value: item, next: nil, previous: currentTail) 123 currentTail.next = newTail 124 self.tail = newTail 125 self.count += 1 126 } 127 128 /// A node of the linked list 129 /// 130 /// Should be `~Copyable` but that would require using a value type such as a struct or enum, and the Swift compiler does not support recursive enums with non-copyable objects for some reason. Example: 131 /// ```swift 132 /// enum List<Y: ~Copyable>: ~Copyable { 133 /// indirect case node(value: Y, next: NewList<Y>) // <-- ERROR: Noncopyable enum 'List' cannot be marked indirect or have indirect cases yet 134 /// case empty 135 /// } 136 /// ``` 137 /// 138 /// Therefore, we make it `private` to make sure we contain the exposure of this unsafe object to only this class. Outside users of the linked list can access objects via the iterator functions. 139 private class Node<Item: ~Copyable> { 140 let value: Item 141 var next: Node? 142 var previous: Node? 143 144 init(value: consuming Item, next: consuming Node?, previous: consuming Node?) { 145 self.value = value 146 self.next = next 147 self.previous = previous 148 } 149 } 150 151 /// A loop command to allow closures to control the loop they are in. 152 enum LoopCommand<Y> { 153 /// Breaks out of the loop 154 case loopBreak 155 /// Continues to the next iteration of the loop 156 case loopContinue 157 /// Stops iterating and return a value 158 case loopReturn(Y) 159 } 160 }