Module genawaiter::rc

source ·
Expand description

This module implements a generator which stores its state on the heap.

You can create a basic generator with gen! and [yield_!].

let mut my_generator = gen!({
    yield_!(10);
});

If you need to reuse logic between multiple generators, you can define the logic with [rc_producer!] and [yield_!], and instantiate generators with Gen::new.

let my_producer = producer!({
    yield_!(10);
});
let mut my_generator = Gen::new(my_producer);

If you don’t like macros, you can use the low-level API directly.

async fn my_producer(co: Co<u8>) {
    co.yield_(10).await;
}
let mut my_generator = Gen::new(my_producer);

§Examples

§Using Iterator

Generators implement Iterator, so you can use them in a for loop:

use genawaiter::{rc::gen, yield_};

let odds_under_ten = gen!({
    let mut n = 1;
    while n < 10 {
        yield_!(n);
        n += 2;
    }
});

for num in odds_under_ten {
    println!("{}", num);
}

§Collecting into a Vec

let xs: Vec<_> = odds_under_ten.into_iter().collect();
assert_eq!(xs, [1, 3, 5, 7, 9]);

§A generator is a closure

Like any closure, you can capture values from outer scopes.

let two = 2;
let mut multiply = gen!({
    yield_!(10 * two);
});
assert_eq!(multiply.resume(), GeneratorState::Yielded(20));

§Using resume()

assert_eq!(odds_under_ten.resume(), GeneratorState::Yielded(1));
assert_eq!(odds_under_ten.resume(), GeneratorState::Yielded(3));
assert_eq!(odds_under_ten.resume(), GeneratorState::Yielded(5));
assert_eq!(odds_under_ten.resume(), GeneratorState::Yielded(7));
assert_eq!(odds_under_ten.resume(), GeneratorState::Yielded(9));
assert_eq!(odds_under_ten.resume(), GeneratorState::Complete(()));

§Passing resume arguments

You can pass values into the generator.

Note that the first resume argument will be lost. This is because at the time the first value is sent, there is no future being awaited inside the generator, so there is no place the value could go where the generator could observe it.

let mut check_numbers = gen!({
    let num = yield_!(());
    assert_eq!(num, 1);

    let num = yield_!(());
    assert_eq!(num, 2);
});

check_numbers.resume_with(0);
check_numbers.resume_with(1);
check_numbers.resume_with(2);

§Returning a completion value

You can return a completion value with a different type than the values that are yielded.

let mut numbers_then_string = gen!({
    yield_!(10);
    yield_!(20);
    "done!"
});

assert_eq!(numbers_then_string.resume(), GeneratorState::Yielded(10));
assert_eq!(numbers_then_string.resume(), GeneratorState::Yielded(20));
assert_eq!(numbers_then_string.resume(), GeneratorState::Complete("done!"));

§Defining a reusable producer function

#[producer_fn(u8)]
async fn produce() {
    yield_!(10);
}

let mut gen = Gen::new(produce);
assert_eq!(gen.resume(), GeneratorState::Yielded(10));

§Defining a reusable producer closure

use genawaiter::rc_producer as producer;

let produce = producer!({
    yield_!(10);
});

let mut gen = Gen::new(produce);
assert_eq!(gen.resume(), GeneratorState::Yielded(10));

§Using the low-level API

You can define an async fn directly, instead of relying on the gen! or producer! macros.

use genawaiter::rc::{Co, Gen};

async fn producer(co: Co<i32>) {
    let mut n = 1;
    while n < 10 {
        co.yield_(n).await;
        n += 2;
    }
}

let odds_under_ten = Gen::new(producer);
let result: Vec<_> = odds_under_ten.into_iter().collect();
assert_eq!(result, [1, 3, 5, 7, 9]);

§Using the low-level API with an async closure (nightly Rust only)

let gen = Gen::new(async move |co| {
    co.yield_(10).await;
    co.yield_(20).await;
});
assert_eq!(gen.resume(), GeneratorState::Yielded(10));
assert_eq!(gen.resume(), GeneratorState::Yielded(20));
assert_eq!(gen.resume(), GeneratorState::Complete(()));

§Using the low-level API with an async closure faux·sure (for stable Rust)

let mut gen = Gen::new(|co| async move {
    co.yield_(10).await;
    co.yield_(20).await;
});
assert_eq!(gen.resume(), GeneratorState::Yielded(10));
assert_eq!(gen.resume(), GeneratorState::Yielded(20));
assert_eq!(gen.resume(), GeneratorState::Complete(()));

§Using the low-level API with function arguments

This is just ordinary Rust, nothing special.

async fn multiples_of(num: i32, co: Co<i32>) {
    let mut cur = num;
    loop {
        co.yield_(cur).await;
        cur += num;
    }
}

let mut gen = Gen::new(|co| multiples_of(10, co));
assert_eq!(gen.resume(), GeneratorState::Yielded(10));
assert_eq!(gen.resume(), GeneratorState::Yielded(20));
assert_eq!(gen.resume(), GeneratorState::Yielded(30));

Structs§

  • This is a generator which stores its state on the heap.

Type Aliases§

  • This object lets you yield values from the generator by calling the yield_ method.