I have an Actor
object that I want to be able to iterate and filter.
actor DataModel {
typealias Details = (passed: Bool, scores: [Int])
private(set) var data: [Int: Details] = [:]
func update(_ value: (Bool, [Int]), forKey key: Int) {
data.updateValue(value, forKey: key)
}
subscript(id: Int) -> Details? {
get {
data[id]
}
set {
data[id] = newValue
}
}
func removeAll() {
data.removeAll()
}
}
extension DataModel: AsyncSequence, AsyncIteratorProtocol {
typealias Element = (key: Int, value: Details)
func next() async throws -> Element? {
var iterator = data.makeIterator()
return iterator.next()
}
nonisolated func makeAsyncIterator() -> Data {
self
}
}
let data = DataModel()
await data.update((false, [1, 2]), forKey: 0)
But, whenever I use the filter
method, it goes into an infinite loop.
let filtered = data.filter { el in
/// infinite loop
return el.value.passed || el.value.scores.count > 3
}
for try await i in filtered {
print(i)
}
Update
Created a separate iterator, but getting the following error:
Actor-isolated property 'data' can not be referenced from a non-isolated context
extension DataDetail: AsyncSequence {
typealias Element = (key: Int, value: (passed: Bool, scores: [Int]))
typealias AsyncIterator = DataInterator
nonisolated func makeAsyncIterator() -> DataInterator {
return DataInterator(data) /// Actor-isolated property 'data' can not be referenced from a non-isolated context
}
}
struct DataInterator: AsyncIteratorProtocol {
typealias Detail = (key: Int, value: (passed: Bool, scores: [Int]))
private let details: [Int: (passed: Bool, scores: [Int])]
lazy var iterator = details.makeIterator()
init(_ details: [Int: (passed: Bool, scores: [Int])]) {
self.details = details
}
mutating func next() async throws -> Detail? {
let nextDetail = iterator.next()
return nextDetail
}
}
CodePudding user response:
You have a mistake in your next()
method. You're creating a new iterator on each call, so every call to your next()
method is effectively returning data.first
over and over again. It'll never hit nil
, so it'll never end.
I'm not sure what the easiest way to fix it is, however. You can't just return data.makeIterator()
from makeAsyncIterator()
, because data
is actor-isolated.
You'll probably want to make a new AsyncIteratorProtocol
-conforming struct which wraps your actor and vends the elements of its data
in an actor-isolated way