5 releases

0.1.4 Dec 17, 2023
0.1.3 Dec 13, 2023
0.1.2 Sep 5, 2023
0.1.1 Sep 5, 2023
0.1.0 Sep 5, 2023

#1667 in Procedural macros

Download history 1/week @ 2023-12-21 87/week @ 2023-12-28 51/week @ 2024-01-04 6/week @ 2024-01-11 5/week @ 2024-01-18 109/week @ 2024-01-25 44/week @ 2024-02-01 44/week @ 2024-02-08 75/week @ 2024-02-15 250/week @ 2024-02-22 121/week @ 2024-02-29 60/week @ 2024-03-07 111/week @ 2024-03-14 73/week @ 2024-03-21 122/week @ 2024-03-28 63/week @ 2024-04-04

397 downloads per month
Used in fauxgen

MIT/Apache

20KB
488 lines

fauxgen - Fake Generators in Stable Rust

This crate allows you to write your own generators in stable rust. It does this by building its own generators on top of async-await.

Getting Started

To get the crate run

cargo add fauxgen

Writing a Generator

This crate provides two different ways to define generators. The first, and most convenient, is as a named top-level function:

#[fauxgen::generator(yield = i32)]
fn generator() {
    r#yield!(1);
    r#yield!(2);
}

and the second is as a lambda using the gen! macro:

use fauxgen::GeneratorToken;

let generator = fauxgen::gen!(|token: GeneratorToken<i32>| {
    token.yield_(1).await;
    token.yield_(2).await;
});

You can also write async generators:

use std::time::Duration;

#[fauxgen::generator(yield = u32)]
async fn generator() {
    for i in 0u32..10 {
        tokio::time::sleep(Duration::from_millis(50)).await;
        r#yield!(i * 2);
    }
}

Using a Generator

Simple generators will implement either Iterator or Stream depending on whether they are async or not. However, in order to be simple enough to do this the generator must not return a value or take an argument. Most generators will likely fall in this category.

Note that because generators are based on async you will need to pin them before they can be used:

#[fauxgen::generator(yield = &'static str)]
fn yield_some_words() {
    r#yield!("testing");
    r#yield!("one");
    r#yield!("two");
}

let gen = std::pin::pin!(yield_some_words());
let words: Vec<_> = gen.collect();
assert_eq!(words, vec!["testing", "one", "two"]);

More advanced generator usage

Generators are not restricted to only yielding values or acting as iteerators. There are actually three different ways to pass values into or out of a generator:

  • the first, which we have already seen, is by yielding a value out of the generator,
  • the second, is by returning a value at the end of the generator,
  • the third, is that external code can pass an argument into the generator resume function. The yield expression then evaluates to this argument.

These can be used together to do more interesting stream processing type work. See the examples folder for some ways of using them.

As a simpler example, this generator will yield back the same value passed to the resume call:

#[fauxgen::generator(yield = T, arg = T)]
fn delay<T>() {
    let mut value = argument!();

    loop {
        value = r#yield!(value);
    }
}

Note the use of the argument! macro in order to grab the first argument to resume. The rest are returned from the yield macro but there is no yield call for the very first argument.

See Also

  • genawaiter is the original "generators on top of async" crate.

Dependencies

~310–760KB
~18K SLoC