---
title: "Async iterator or Stream exploration doc"
tags: ["WG-async", "analysis"]
date: 2024-01-05
url: https://hackmd.io/1jmGsVS_Tv-4V7QdKyRldA
---
# Async iteratation exploration doc
## What is this
A document to explore the design space for async iterators / streams (there may or may not be a single concept here!).
## Code examples
This section lists out various properties that the design could support. They are not ordered, and they may or may not all be things that we could satisfy simultaneously. Not everybody necessarily wants all of them.
### Process-each-item
We want people to be able to iterate and process each item in the stream.
```rust
// something like this....
for await item in async_iter {
...
}
```
Another option is a `for_each` method:
```rust
async_iter.for_each(async |item| {
})
```
### While let
Analogy to sync iterators suggests people may attempt to write things like this
```rust!
while let Some(item) = async_iter.next().await {
....
}
```
### First-of-many-things
It's common to want to process each item in an async iterator but also do other things in the meantime, or to merge multiple iterators and take the first item available from any of them.
One way to do this today is select:
```rust
select! {
iter1.next() => ...
iter2.next() => ...
}
```
Yosh highlighted another option, [merging streams](https://blog.yoshuawuyts.com/futures-concurrency-3/#concurrent-stream-processing-with-stream-merge):
```rust
iter1.merge(iter2)
```
### Async generators passed to a function
It should be possible to call functions with generators...
```rust
foo(async gen {
...
})
```
...where those functions are declared in some normalish way:
```rust
fn foo(x: impl AsyncIterator<Item = ...>) {...}
// ^^^^^^^^^^^^^ maybe `IntoAsyncIterator`?
```
### Sync generators passed to a function
Sync version of the above would probably be....
```rust
foo(gen {
...
})
```
and
```rust
fn foo(x: impl IntoIterator<Item = ...>) {...}
```
### Borrow across a yield in async (or sync) generator
```rust
async gen {
for x in &mut something {
yield x.some_mut_method()
}
}
```
or
```rust
gen {
for x in &mut something {
yield x.some_mut_method()
}
}
```
## Desired properties
This section lists out various properties that the design could have. They are not ordered, and they may or may not all be things that we could satisfy simultaneously. Not everybody necessarily wants all of them.
### Analogous with sync iterators
All things being equal, it's better if "async patterns" and "sync patterns" are clearly analogous.
### Easy to have async generators
### Easy to manually implement
It'd be nice if it were "as easy" to implement async iterators as it is sync ones. That said, generators may make this less important.
```rust
struct AsyncRange {
from: u32,
to: u32,
}
impl AsyncIterator for AsyncRange {
async fn next(&mut self) -> Option<u32> {
if self.from >= self.to {
None
} else {
let f = self.from;
self.from += 1;
Some(f)
}
}
}
```
### Virtual dispatch
We want people to be able to use `dyn TheTrait` with minimal overhead. Example:
```rust
fn
```
### Cancel safety of the `next` option
With futures streams, it is common to combine `next` futures and select:
```rust!
let x: impl Stream = ...;
select! {
x.next() => ...
other_stuff => ...
} // I know I'm getting the syntax wrong...
```
this is convenient because it allows you to respond to the "next item" in the stream or
This may however be less important when not relying on `select!` for concurrency, but using dedicated async concurrency operations instead.
### Guarantee forwarding `async Drop`
If a type implements `async Drop`, it should be possible to hold inside of an async iterator impl, and guarantee it is forwarded. That should also be the case for async `for..in` loops and async generator blocks and functions.
### Composition with lending, fallibility, pinning, non-send, etc.
We know there is ecosystem demand for lending versions of iterators, fallible iterators, pinned iterators, and more. We should have a way to provide all of these in a way that ends up feeling consistent
## Draft designs
## Appendix: Related work
* The existing Stream trait from futures crate
* Rayon's ParallelIterator trait
* Kotlin's [asynchronous flows](https://kotlinlang.org/docs/flow.html)
* [`async-iterator` crate](https://docs.rs/async-iterator/latest/async_iterator/index.html)
* [`lending-iterator` crate](https://docs.rs/lending-iterator/latest/lending_iterator/)
* [`fallible-iterator` crate](https://docs.rs/fallible-iterator/latest/fallible_iterator/index.html)
* Something from Swift??
* Other things??
## Appendix: Links to blog posts