[−][src]Module genawaiter::stack
This module implements a generator which doesn't require allocation.
You can create a generator with the unsafe_create_generator!
macro:
async fn producer(co: Co<'_, i32>) { /* ... */ } unsafe_create_generator!(gen, producer);
This is safe as long as you don't do anything silly with the Co
object. (See below for
the fine print. If you cannot abide the unsafe
keyword, use an allocating
generator instead.)
The macro is a shortcut for creating both a generator and its backing state (called a
Shelf
, to avoid confusion with the GeneratorState
enum). If you (or your IDE)
dislike macros, you can also do the bookkeeping by hand:
let mut shelf = Shelf::new(); let gen = unsafe { Gen::new(&mut shelf, producer) };
See the crate-level docs for a guide on how to use the generator after it's been created.
Safety
Do not let the Co
object escape the scope of the generator. By time the generator
completes, the Co
object should already have been dropped. If this invariant is not
upheld, memory unsafety will result.
Afaik, the Rust compiler is not flexible enough to let you express this invariant in the type system, but I would love to be proven wrong!
Examples
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.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)
unsafe_create_generator!(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)
unsafe_create_generator!(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; } } unsafe_create_generator!(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); } unsafe_create_generator!(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!" } unsafe_create_generator!(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
Co | This object lets you yield values from the generator by calling the |
Gen | This is a generator which stores all its state without any allocation. |
Shelf | This data structure holds the transient state of an executing generator. |