| Crates.io | async_to_iter |
| lib.rs | async_to_iter |
| version | 0.1.0 |
| created_at | 2024-08-16 17:13:32.537446+00 |
| updated_at | 2024-08-16 17:13:32.537446+00 |
| description | Convert async functions to generators on stable Rust |
| homepage | |
| repository | |
| max_upload_size | |
| id | 1340500 |
| size | 24,342 |
This crate allows to write generator-like async code to implement Iterator
on today's (August 2024) stable Rust.
This crate is dual-licensed under the terms of both Apache 2.0 and MIT licenses.
use async_to_iter::{IterSink, make_iter};
// Async code that implements the iterator.
async fn count_to_impl(sink: IterSink<u32>, n: u32) {
for i in 1..=n {
sink.yield_value(i).await;
}
}
// Function that constructs the iterator from async code using `make_iter()`.
fn count_to(n: u32) -> impl Iterator<Item = u32> {
make_iter(move |sink| count_to_impl(sink, n))
}
fn main() {
// The resulting iterator can be accessed as usual.
let mut iter = count_to(3);
assert_eq!(iter.next(), Some(1));
assert_eq!(iter.next(), Some(2));
assert_eq!(iter.next(), Some(3));
assert_eq!(iter.next(), None);
}
The compiler automatically converts async code to a state machine that saves its internal
state across await points. This is how async code has long been implemented in Rust.
make_iter returns a type implementing Iterator that translates Iterator::next() to Future::poll() calls.
Yielding value and suspending the future in yield points is implemented by IterSink::yield_value().
It saves the value provided by async code (it will later be returned from Iterator::next())
and returns a Future that becomes ready the second time Future::poll is called.
This way, execution of async code pauses exactly once each time a value is yielded.
unsafe?It uses it just for one thing: to create a no-op Waker.
It is safe because a no-op waker does nothing, and it actually re-implements an unstable safe functions
from the standard library: Waker::noop().
The rest of this crate only uses safe Rust code.
Unfortunately, no. The future that yields iterator output values is stored on heap — this is necessary to
pin it without affecting the usability of the Iterator implementation returned from make_iter().
It also makes some other allocations now. The number of allocations may be optimized in the future,
but it is unlikely that this will become a zero-cost abstraction.
If you need zero-cost generators in Rust, you will likely have to use some Nightly features.
#[no_std] environments?Yes, this crate is #[no_std]. However, it needs the alloc crate and a global allocator.