[−][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 finalGeneratorState::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 byCo::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)
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 |
Gen | This is a generator which stores all its state without any allocation. |