[][src]Module genawaiter::stack

This module implements a generator which doesn't allocate.

You can create a basic generator with let_gen! and yield_!.

let_gen!(my_generator, {
    yield_!(10);
});

If you don't like macros, you can use the low-level API directly, though note that this requires you to trade away safety.

async fn my_producer(co: Co<'_, u8>) {
    co.yield_(10).await;
}
let mut shelf = Shelf::new();
let mut my_generator = unsafe { Gen::new(&mut shelf, my_producer) };

Examples

Using Iterator

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

use genawaiter::{stack::let_gen, yield_};

let_gen!(odds_under_ten, {
    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_gen!(multiply, {
    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_gen!(check_numbers, {
    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_gen!(numbers_then_string, {
    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_gen_using!(gen, 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::stack::{let_gen_using, Co};

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

let_gen_using!(odds_under_ten, 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)

This example is not tested
let_gen_using!(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 the low-level API with an async closure faux·sure (for stable Rust)

let_gen_using!(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(()));

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_gen_using!(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));

Macros

let_gen

Creates a generator.

let_gen_using

Creates a generator using a producer defined elsewhere.

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.

Attribute Macros

producer_fn

Turns a function into a producer, which can then be used to create a generator.