I think I found a workaround, but kinda wordy
// Basic trick
protocol AsyncThrowingIteratorProtocol<Element>: AsyncIteratorProtocol {}
protocol AsyncNonThrowingIteratorProtocol<Element>: AsyncIteratorProtocol {
mutating func next() async -> Element?
}
protocol AsyncSequenceOf<Element>: AsyncSequence {}
protocol AsyncNonThrowingSequenceOf<Element>: AsyncSequenceOf where Self.AsyncIterator: AsyncNonThrowingIteratorProtocol {}
typealias AsyncStreamAsyncIterator<T> = AsyncStream<T>.AsyncIterator
extension AsyncStreamAsyncIterator: AsyncNonThrowingIteratorProtocol {}
extension AsyncStream: AsyncNonThrowingSequenceOf {}
extension AsyncThrowingStream: AsyncSequenceOf {}
// Support for `.prefix(_ count:Int)`
typealias AsyncPrefixSequenceIterator<T: AsyncSequence> = AsyncPrefixSequence<T>.Iterator
extension AsyncPrefixSequenceIterator: AsyncThrowingIteratorProtocol {}
extension AsyncPrefixSequenceIterator: AsyncNonThrowingIteratorProtocol where Base: AsyncNonThrowingSequenceOf {
mutating func next() async -> Base.Element? {
/// Looks like a bug in the compiler. It has enough info
/// to deduce this by itself.
/// But for now we have to do it manually.
/// Here we must call the implementation of
/// `AsyncThrowingIteratorProtocol<Base.Element>.next` on `self`.
/// But there is no syntax to express something like:
/// `self.(AsyncThrowingIteratorProtocol.next)()`
/// At least to my knowlendge.
/// So instead we erase `self` to the desired protocol via opaque,
/// call the function and apply changes made on the opaque back to `self`.
var s: some AsyncThrowingIteratorProtocol<Base.Element> = self
defer { self = unsafeBitCast(s, to: Self.self) }
return try! await s.next()
}
}
extension AsyncPrefixSequence: AsyncSequenceOf {}
extension AsyncPrefixSequence: AsyncNonThrowingSequenceOf where Base: AsyncNonThrowingSequenceOf {}
// Example
var s1: some AsyncNonThrowingSequenceOf<Int> {
AsyncStream<Int> {
try! await Task.sleep(nanoseconds: 1_000_000_000)
return Int.random(in: 0...100)
}.prefix(10)
}
var s2: some AsyncSequenceOf<Int> {
AsyncThrowingStream<Int, Error> {
try await Task.sleep(nanoseconds: 1_000_000_000)
return Int.random(in: 0...100)
}.prefix(10)
}
var it = s1.prefix(3).makeAsyncIterator()
while let i = await it.next() { print(i) } // OK
//for await i in s1.prefix(10) { } // Error: "Call can throw, but the error is not handled" :( Seems like a bug in the compiler
for try await i in s2.prefix(3) { print(i) } // OK