[][src]Module genawaiter::stack

This module implements a generator which is allocation-free.

You can create a generator with the unsafe_create_generator! macro. This is safe as long as you don't do anything unusual with the Co object. (See below for the fint print.) If unsafety is not tolerable, use [rc::Gen] instead.

Pass the macro a callable expression which accepts a Co object. Values can be yielded from the generator by calling [Co::yield_][stack::Co::yield_], and immediately awaiting the future it returns:

co.yield_("value").await;

You can get values out of the generator in either of two ways:

  • Treat it as an iterator. In this case, the future's output must be ().
  • Call resume() until it completes. In this case, the future's output can be anything, and it will be returned in the final GeneratorState::Complete.

If you do not follow the yield_().await pattern above, behavior is memory-safe but otherwise left unspecified. Specifically, follow these guidelines to remain on the happy path:

  • Whenever calling yield_(), always immediately await its result.
  • Do not await any futures other than ones returned by Co::yield_.

Safety

Do not let the Co object escape the scope of the generator. Once the starting future returns Poll::Ready, the Co object should already have been dropped. If this invariant is not upheld, memory unsafety will result.

Afaik, Rust's type system does not let you express the necessary lifetime bounds to guarantee safety, but I would love to be proven wrong!

Examples

(See the crate-level docs for the definition of odd_numbers_less_than_ten.)

Using Iterator

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

unsafe_create_generator!(gen, odd_numbers_less_than_ten);
for n in gen {
    println!("{}", n);
}

Collecting into a Vec

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

Using resume()

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

Using an async closure (nightly only)

This example deliberately fails to compile
unsafe_create_generator!(gen, async move |co| {
    co.yield_(10).await;
    co.yield_(20).await;
});
assert_eq!(gen.as_mut().resume(), GeneratorState::Yielded(10));
assert_eq!(gen.as_mut().resume(), GeneratorState::Yielded(20));
assert_eq!(gen.as_mut().resume(), GeneratorState::Complete(()));

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

unsafe_create_generator!(gen, |co| multiples_of(10, co));
assert_eq!(gen.as_mut().resume(), GeneratorState::Yielded(10));
assert_eq!(gen.as_mut().resume(), GeneratorState::Yielded(20));
assert_eq!(gen.as_mut().resume(), GeneratorState::Yielded(30));

Returning a final value

You can return a final 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!"
}

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

Structs

Co

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

Gen

This is a generator which stores all its state without any allocation.