Crate remit

source ·
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 'static lifetime can only be used when boxed.

Traits

  • Trait used for relaxing the lifetime requirements of the generator storage.