Expand description
Generators implemented through async/await syntax.
The pinned implementation is stack-based, and the boxed is heap-based. No fancy macros and a simple API. Values can be lazily or eagerly yielded.
Examples
General usage of unbounded generator.
use std::pin::pin;
use remit::{Generator, Remit};
async fn gen(remit: Remit<'_, usize>) {
remit.value(42).await;
// Does not need to be limited
for i in 1.. {
remit.value(i).await
}
}
for item in pin!(Generator::new()).of(gen).take(10) {
println!("{item}");
// Prints 42, 1, 2, 3, 4, 5, 6, 7, 8, 9
}
assert_eq!(vec![42, 1, 2, 3], pin!(Generator::new()).of(gen).take(4).collect::<Vec<_>>());
/*
// Rust has trouble determining the lifetime
assert_eq!(
vec![1],
pin!(Generator::new())
.of(|remit: Remit<'_, usize>| async move { remit.value(1).await; })
.collect::<Vec<_>>(),
);
*/
assert_eq!(vec![42, 1], Generator::boxed(gen).take(2).collect::<Vec<_>>());
fn iter() -> impl Iterator<Item=usize> {
Generator::boxed(gen)
}Parameterized usage.
async fn scream<D: fmt::Display>(iter: impl Iterator<Item=D>, remit: Remit<'_, String>) {
for person in iter {
remit.value(format!("{person} scream!")).await
}
remit.value("... for ice cream!".to_string());
}
let expected: Vec<String> = ["You scream!", "I scream!", "We all scream!", "... for ice cream!"].iter().map(ToString::to_string).collect();
assert_eq!(
expected,
pin!(Generator::new()).parameterized(scream, ["You", "I", "We all"].iter()).collect::<Vec<String>>(),
);
assert_eq!(
expected,
Generator::boxed(|remit| scream(["You", "I", "We all"].iter(), remit)).collect::<Vec<String>>(),
);Usage of a generator that only functions for 'static.
use remit::{Generator, Remit};
async fn gen(remit: Remit<'static, usize>) {
remit.value(2).await;
remit.value(3).await;
remit.value(5).await;
remit.value(7).await;
}
for item in Generator::boxed(gen) {
println!("{item}");
}
assert_eq!(vec![2, 3, 5, 7], Generator::boxed(gen).collect::<Vec<_>>());
assert_eq!(vec![1], Generator::boxed(|remit| async move { remit.value(1).await; }).collect::<Vec<_>>());
fn iter() -> impl Iterator<Item=usize> {
Generator::boxed(gen)
}Unorthodox usage of “eagerly” yielding values.
// These implementations run successfully.
// However, they trigger creation of a buffer.
async fn no_await(remit: Remit<'_, usize>) {
let _discard_future = remit.value(2);
let _discard_future = remit.value(3);
let _discard_future = remit.value(5);
let _discard_future = remit.value(7);
}
assert_eq!(vec![2, 3, 5, 7], pin!(Generator::new()).of(no_await).collect::<Vec<_>>());
async fn delay_await(remit: Remit<'_, usize>) {
let first_remit = remit.value(11);
remit.value(13).await;
// Will poll-ready as the latter call implies all values are consumed.
// A join will also do the same.
first_remit.await;
let _ = remit.value(17);
let _ = remit.value(19);
// Even though the future is done, the values were already sent.
}
assert_eq!(vec![11, 13, 17, 19], pin!(Generator::new()).of(delay_await).collect::<Vec<_>>());Incorrect attempt of a stack-based generator.
ⓘ
/// Only accepts `'static`, so it needs to be boxed.
async fn gen(remit: Remit<'static, usize>) {
remit.value(1).await;
}
// Fails to compile, because gen is only `'static` and pinning is for the stack.
for item in pin!(Generator::new()).of(gen) {
println!("{item}");
}Structs
- The storage used for iterators that poll a generator.
- An iterator over generated values.
- Allows a generator to provide values to an iterator. A generator that only accepts the
'staticlifetime can only be used when boxed.
Traits
- Trait used for relaxing the lifetime requirements of the generator storage.