1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
/*! 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: ```rust # use genawaiter::stack::Co; # # async fn f(co: Co<'_, &str>) { 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][hrtb-thread] the necessary lifetime bounds to guarantee safety, but I would love to be proven wrong! [hrtb-thread]: https://users.rust-lang.org/t/hrtb-on-multiple-generics/34255 # 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: ```rust # use genawaiter::{stack::{Co, Gen}, unsafe_create_generator, GeneratorState}; # # async fn odd_numbers_less_than_ten(co: Co<'_, i32>) { # for n in (1..).step_by(2).take_while(|&n| n < 10) { co.yield_(n).await; } # } # unsafe_create_generator!(gen, odd_numbers_less_than_ten); for n in gen { println!("{}", n); } ``` ## Collecting into a `Vec` ```rust # use genawaiter::{stack::{Co, Gen}, unsafe_create_generator, GeneratorState}; # # async fn odd_numbers_less_than_ten(co: Co<'_, i32>) { # for n in (1..).step_by(2).take_while(|&n| n < 10) { co.yield_(n).await; } # } # 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()` ```rust # use genawaiter::{stack::{Co, Gen}, unsafe_create_generator, GeneratorState}; # # async fn odd_numbers_less_than_ten(co: Co<'_, i32>) { # for n in (1..).step_by(2).take_while(|&n| n < 10) { co.yield_(n).await; } # } # 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) ```compile_fail # use genawaiter::{stack::{Co, Gen}, unsafe_create_generator, GeneratorState}; # 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. ```rust # use genawaiter::{stack::{Co, Gen}, unsafe_create_generator, GeneratorState}; # 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. ```rust # use genawaiter::{stack::{Co, Gen}, unsafe_create_generator, GeneratorState}; # 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!")); ``` */ pub use engine::Co; pub use generator::Gen; #[macro_use] mod macros; mod engine; mod generator; mod iterator; #[cfg(feature = "nightly")] #[cfg(test)] mod nightly_tests; #[cfg(test)] mod tests { use crate::{stack::Co, GeneratorState}; async fn simple_producer(c: Co<'_, i32>) -> &'static str { c.yield_(10).await; "done" } #[test] fn function() { unsafe_create_generator!(gen, simple_producer); assert_eq!(gen.as_mut().resume(), GeneratorState::Yielded(10)); assert_eq!(gen.as_mut().resume(), GeneratorState::Complete("done")); } #[test] fn simple_closure() { async fn gen(i: i32, co: Co<'_, i32>) -> &'static str { co.yield_(i * 2).await; "done" } unsafe_create_generator!(gen, |co| gen(5, co)); assert_eq!(gen.as_mut().resume(), GeneratorState::Yielded(10)); assert_eq!(gen.as_mut().resume(), GeneratorState::Complete("done")); } /// This test proves that `unsafe_create_generator` is actually unsafe. #[test] #[ignore = "compile-only test"] fn unsafety() { async fn shenanigans(co: Co<'_, i32>) -> Co<'_, i32> { co } fn co_escape() -> Co<'static, i32> { unsafe_create_generator!(gen, shenanigans); // Returning `co` from this function violates memory safety. match gen.as_mut().resume() { GeneratorState::Yielded(_) => panic!(), GeneratorState::Complete(co) => co, } } let co = co_escape(); // `co` points to data which was on the stack of `co_escape()` and has been // dropped. let _ = co.yield_(10); } }