[][src]Module genawaiter::stack

This module implements a generator which doesn't allocate.

You can create a generator with the generator_mut! macro:

async fn producer(co: Co<'_, i32>) { /* ... */ }

generator_mut!(my_generator, producer);

This will create a variable named my_generator in the current scope, with type &mut Gen<...>.

The macro is a shortcut for creating both a generator and its backing state (called a Shelf). If you (or your IDE) dislike macros, you can also do the bookkeeping by hand by using Gen::new, though note that this requires you to trade away safety.

Examples

Using Iterator

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

use genawaiter::{generator_mut, stack::Co};

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

generator_mut!(gen, odd_numbers_less_than_ten);
for num in gen {
    println!("{}", num);
}

Collecting into a Vec

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

Using resume()

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

Using an async closure (nightly Rust only)

This example is not tested
generator_mut!(gen, 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 an async closure faux·sure (works on stable Rust)

generator_mut!(gen, |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(()));

Passing ordinary 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;
    }
}

generator_mut!(gen, |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));

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.

async fn check_numbers(co: Co<'_, (), i32>) {
    let num = co.yield_(()).await;
    assert_eq!(num, 1);

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

generator_mut!(gen, check_numbers);
gen.resume_with(0);
gen.resume_with(1);
gen.resume_with(2);

Returning a completion value

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

async fn numbers_then_string(co: Co<'_, i32>) -> &'static str {
    co.yield_(10).await;
    co.yield_(20).await;
    "done!"
}

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

Structs

Gen

This is a generator which can be stack-allocated.

Shelf

This data structure holds the transient state of an executing generator.

Type Definitions

Co

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