Calling mutating async function from an actor

I’m working with a single AsyncSequence that needs to be consumed by two other ASs. To coordinate this, I set up an actor that owns the underlying iterator and exposes methods that forward next() calls. (Following is just an example of interest to the conversation, see Add streamable multipart part by ptoffy · Pull Request #145 · vapor/multipart-kit · GitHub for the full code)

@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
actor WrappedIterator<BackingSequence: AsyncSequence>
where BackingSequence.Element == [UInt8] {
    private var backingIterator: BackingSequence.AsyncIterator

    init(backingIterator: BackingSequence.AsyncIterator) {
        self.backingIterator = backingIterator
    }

    func nextChunk() async throws -> BackingSequence.Element? {
        try await backingIterator.next(isolation: self) // 🚨
    }
}

This doesn't compile:

Cannot call mutating async function 'next(isolation:)' on actor-isolated property 'backingIterator'

I can appreciate that this protects from re-entrancy as the await might allow mutation of backingIterator from another actor method, however the only "safe" fix I've found to solve the issue is replacing nextChunk() with

func nextChunk() async throws -> BackingSequence.Element? {
    var temp = backingIterator
    let next = try await temp.next(isolation: self)
    backingIterator = temp
    return next
}

This is not ideal for performance reasons. The other solution would be to wrap the backingIterator in a class which would defy isolation altogether.

Is there an actor-safe way to call a mutating async method on a stored property? Or would any other solution just be slower than simply copying the iterator?

if the state that you want exclusive access to during the mutating async call is actor-isolated, i don't think so – at least that's the impression i have, based on this: Rationale for banning actor-isolated state from being passed `inout` to async functions?.

IMO reusing the same iterator across multiple Tasks or iterating the same async sequence concurrently can lead to very confusing behaviors – is that what you want to support in this case? i found this related-seeming comment in a PR from the async-algorithms repo interesting. IIUC the gist is that in many (most?) cases, an AsyncSequence itself can be Sendable, but its AsyncIterator typically is not or should not be, to enforce use from a single Task at a time (it's possible i'm misinterpreting, perhaps @FranzBusch can weigh in).

going back to your use case – why is it that you want to forward to the same base iterator vs having a separate one for each of your consuming sequences?

So essentially what I'm trying to do is, given a single source sequence (e.g AsyncSequence<[UInt8]>) build another sequence (let's call it PartAsyncSequence) that returns structured parts out of the original one:

struct Part {
    let body: PartBodyAsyncSequence<[UInt8]>
    // ... other fields
}

You'll notice how the Part itself also holds a sequence. To be able to also consume that sequence, I need some kind of coordination to correctly read from the original one, since both sub-sequences will consume the upstream one.
If I'm consuming a PartAsyncSequence, I want my iterator to return the whole Part, in contrast if I'm reading from PartBodyAsyncSequence, I want the iterator to return single body chunks. I hope this makes sense.

I'm more than happy to consider alternative solutions, the shared iterator actor was just my first instinct.