1 unstable release

0.1.0 Jan 13, 2024

#1326 in Asynchronous

MIT/Apache

67KB
2K SLoC

Async Iter

This library offers an asynchronous version of the rust's Iterator trait and utilities.

Quick Start

Add to Dependencies

Add async-iter to your dependencies:

cargo add asynciter

Entrance to Utilities

Then, import the basic traits:

use asynciter::{
    // This offers standard api, just like the `std::iter::Iterator`
    AsyncIterator,
    // use `.aiter()` to convert a synchronous iterator
    // into an asynchronous one
    ToAsyncIterator,
    // use `.into_aiter()` to convert a synchronous iterable object
    // into an asynchronous iterator
    IntoAsyncIterator,
}

Api Call Stack

All api names and restrictions are the same as the std ones. You just need to change your .iter() or .into_iter() to .aiter() or .into_aiter():

assert_eq!(
    ["Hello%", "World%", "!"]
        .into_aiter()
        .map(|s| s.to_ascii_lowercase())
        .intersperse(" ".into())
        .flat_map(|s| s.chars().filter(|s| s != &'%').collect::<Vec<_>>())
        .collect::<String>()
        .await,
    "hello world !"
);

Asynchronous Alternative

If you want to use asynchronous functions inside your iterators, api prefixed with a will accept a function returning a Future while works the same as synchronous ones:

async fn do_async(val: i32) -> String {
    format!("Async! {val}")
}

(0..=10)
    .aiter()
    // `amap` accepts `FnMut(Item) -> Future<Output>`
    .amap(do_async)
    // `afor_each` accepts `FnMut(Item) -> Future<Output = ()>`
    .afor_each(|s| async move {
        println!("{s}");
    })
    .await;

Awaited Asynchronous Iterator

If you have an iterator which yields Futures, use .awaited() to change them into an AsyncIterator:

// impl Iterator<Output: Future<Output = i32>>
let sync_it = (0..10).map(|s| async move { s + 1 });
// impl AsyncIterator<Output = i32>
let async_it = sync_it.aiter().awaited();

Internal Implementation

All internal implementations can be found in the asynciter::iterimpl module, and are exported publicly, but direct usage is not recommended.

Notes

For those api which accept FnXXX(&Self::Item) -> Future, there's another extra restriction. Since the Future will be used later than the function call, the reference might outlive the function call's lifetime, leading to compiler failures. To keep code safe, the reference must be processed and dropped outside of the Future.

For example, the following code will not pass the compiler check:

(0..10)
    .aiter()
    .afilter(
        |s| // Here we accept the reference
        async move {
            *s < 5 // And we use it here.
            // The reference will live longer than the function,
            // which is not safe.
        }
    )

Instead, we must use the following solution:

(0..10)
    .map(|s| s.to_string())
    .aiter()
    .afilter(|s| {
        let s = s.clone(); // Here we save the reference
                           // by cloning the value
        async move {
            // do something
            false
        }
    })
    .for_each(|_| {}).await;

License

This project is licensed under the MIT License or Apache V2.0 License.

No runtime deps