How it feels to use

planning rfc

Let's start with how we expect to use dyn AsyncIterator. This section will also elaborate some of our desiderata1, such as the ability to use dyn AsyncIterator conveniently in both std and no-std scenarios.

1

Ever since I once saw Dave Herman use this bizarre latin plural, I've been in love with it. --nikomatsakis

How you write a function with a dyn argument

We expect people to be able to write functions that take a dyn AsyncIterator trait as argument in the usual way:


#![allow(unused)]
fn main() {
async fn count(i: &mut dyn AsyncIterator) -> usize {
    let mut count = 0;
    while let Some(_) = i.next().await {
        count += 1;
    }
    count
}
}

One key part of this is that we want count to be invokable from both a std and a no-std environment.

How you implement a trait with async fns

This, too, looks like you would expect.


#![allow(unused)]
fn main() {
struct YieldingRangeIterator {
    start: u32,
    stop: u32,
}

impl AsyncIterator for YieldingRangeIterator {
    type Item = u32;

    async fn next(&mut self) {
        if self.start < self.stop {
            let i = self.start;
            self.start += 1;
            tokio::thread::yield_now().await;
            Some(i)
        } else {
            None
        }
    }
}
}

How you invoke count in std

You invoke it as you normally would, by performing an unsize coercion. Invoking the method requires an allocator by default.


#![allow(unused)]
fn main() {
let x = YieldingRangeIterator::new(...);
let c = count(&mut x /* as &mut dyn AsyncIterator */).await;
}