genawaiter/rc/mod.rs
1/*!
2This module implements a generator which stores its state on the heap.
3
4You can create a basic generator with [`gen!`] and [`yield_!`].
5
6[`gen!`]: macro.gen.html
7
8```rust
9# #[cfg(feature = "proc_macro")]
10# fn feature_gate() {
11# use genawaiter::{rc::gen, yield_};
12#
13let mut my_generator = gen!({
14 yield_!(10);
15});
16# my_generator.resume();
17# }
18```
19
20If you need to reuse logic between multiple generators, you can define the logic with
21[`rc_producer!`] and [`yield_!`], and instantiate generators with [`Gen::new`].
22
23```rust
24# #[cfg(feature = "proc_macro")]
25# fn feature_gate() {
26# use genawaiter::{rc::Gen, rc_producer as producer, yield_};
27#
28let my_producer = producer!({
29 yield_!(10);
30});
31let mut my_generator = Gen::new(my_producer);
32# my_generator.resume();
33# }
34```
35
36If you don't like macros, you can use the low-level API directly.
37
38```rust
39# use genawaiter::rc::{Co, Gen};
40#
41async fn my_producer(co: Co<u8>) {
42 co.yield_(10).await;
43}
44let mut my_generator = Gen::new(my_producer);
45# my_generator.resume();
46```
47
48# Examples
49
50## Using `Iterator`
51
52Generators implement `Iterator`, so you can use them in a for loop:
53
54```rust
55# #[cfg(feature = "proc_macro")]
56# fn feature_gate() {
57use genawaiter::{rc::gen, yield_};
58
59let odds_under_ten = gen!({
60 let mut n = 1;
61 while n < 10 {
62 yield_!(n);
63 n += 2;
64 }
65});
66
67# let mut test = Vec::new();
68for num in odds_under_ten {
69 println!("{}", num);
70 # test.push(num);
71}
72# assert_eq!(test, [1, 3, 5, 7, 9]);
73# }
74```
75
76## Collecting into a `Vec`
77
78```rust
79# #[cfg(feature = "proc_macro")]
80# fn feature_gate() {
81# use genawaiter::{rc::gen, yield_};
82#
83# let odds_under_ten = gen!({
84# for n in (1..).step_by(2).take_while(|&n| n < 10) { yield_!(n); }
85# });
86#
87let xs: Vec<_> = odds_under_ten.into_iter().collect();
88assert_eq!(xs, [1, 3, 5, 7, 9]);
89# }
90```
91
92## A generator is a closure
93
94Like any closure, you can capture values from outer scopes.
95
96```rust
97# #[cfg(feature = "proc_macro")]
98# fn feature_gate() {
99# use genawaiter::{rc::gen, yield_, GeneratorState};
100#
101let two = 2;
102let mut multiply = gen!({
103 yield_!(10 * two);
104});
105assert_eq!(multiply.resume(), GeneratorState::Yielded(20));
106# }
107```
108
109## Using `resume()`
110
111```rust
112# #[cfg(feature = "proc_macro")]
113# fn feature_gate() {
114# use genawaiter::{rc::gen, yield_, GeneratorState};
115#
116# let mut odds_under_ten = gen!({
117# for n in (1..).step_by(2).take_while(|&n| n < 10) { yield_!(n); }
118# });
119#
120assert_eq!(odds_under_ten.resume(), GeneratorState::Yielded(1));
121assert_eq!(odds_under_ten.resume(), GeneratorState::Yielded(3));
122assert_eq!(odds_under_ten.resume(), GeneratorState::Yielded(5));
123assert_eq!(odds_under_ten.resume(), GeneratorState::Yielded(7));
124assert_eq!(odds_under_ten.resume(), GeneratorState::Yielded(9));
125assert_eq!(odds_under_ten.resume(), GeneratorState::Complete(()));
126# }
127```
128
129## Passing resume arguments
130
131You can pass values into the generator.
132
133Note that the first resume argument will be lost. This is because at the time the first
134value is sent, there is no future being awaited inside the generator, so there is no
135place the value could go where the generator could observe it.
136
137```rust
138# #[cfg(feature = "proc_macro")]
139# fn feature_gate() {
140# use genawaiter::{rc::gen, yield_};
141#
142let mut check_numbers = gen!({
143 let num = yield_!(());
144 assert_eq!(num, 1);
145
146 let num = yield_!(());
147 assert_eq!(num, 2);
148});
149
150check_numbers.resume_with(0);
151check_numbers.resume_with(1);
152check_numbers.resume_with(2);
153# }
154```
155
156## Returning a completion value
157
158You can return a completion value with a different type than the values that are
159yielded.
160
161```rust
162# #[cfg(feature = "proc_macro")]
163# fn feature_gate() {
164# use genawaiter::{rc::gen, yield_, GeneratorState};
165#
166let mut numbers_then_string = gen!({
167 yield_!(10);
168 yield_!(20);
169 "done!"
170});
171
172assert_eq!(numbers_then_string.resume(), GeneratorState::Yielded(10));
173assert_eq!(numbers_then_string.resume(), GeneratorState::Yielded(20));
174assert_eq!(numbers_then_string.resume(), GeneratorState::Complete("done!"));
175# }
176```
177
178## Defining a reusable producer function
179
180```rust
181# #[cfg(feature = "proc_macro")]
182# fn feature_gate() {
183# use genawaiter::{rc::{producer_fn, Gen}, yield_, GeneratorState};
184#
185#[producer_fn(u8)]
186async fn produce() {
187 yield_!(10);
188}
189
190let mut gen = Gen::new(produce);
191assert_eq!(gen.resume(), GeneratorState::Yielded(10));
192# }
193```
194
195## Defining a reusable producer closure
196
197```rust
198# #[cfg(feature = "proc_macro")]
199# fn feature_gate() {
200# use genawaiter::{rc::Gen, yield_, GeneratorState};
201use genawaiter::rc_producer as producer;
202
203let produce = producer!({
204 yield_!(10);
205});
206
207let mut gen = Gen::new(produce);
208assert_eq!(gen.resume(), GeneratorState::Yielded(10));
209# }
210```
211
212## Using the low-level API
213
214You can define an `async fn` directly, instead of relying on the `gen!` or `producer!`
215macros.
216
217```rust
218use genawaiter::rc::{Co, Gen};
219
220async fn producer(co: Co<i32>) {
221 let mut n = 1;
222 while n < 10 {
223 co.yield_(n).await;
224 n += 2;
225 }
226}
227
228let odds_under_ten = Gen::new(producer);
229let result: Vec<_> = odds_under_ten.into_iter().collect();
230assert_eq!(result, [1, 3, 5, 7, 9]);
231```
232
233## Using the low-level API with an async closure (nightly Rust only)
234
235```ignore
236# use genawaiter::{rc::Gen, GeneratorState};
237#
238let gen = Gen::new(async move |co| {
239 co.yield_(10).await;
240 co.yield_(20).await;
241});
242assert_eq!(gen.resume(), GeneratorState::Yielded(10));
243assert_eq!(gen.resume(), GeneratorState::Yielded(20));
244assert_eq!(gen.resume(), GeneratorState::Complete(()));
245```
246
247## Using the low-level API with an async <del>closure</del> faux·sure (for stable Rust)
248
249```
250# use genawaiter::{rc::Gen, GeneratorState};
251#
252let mut gen = Gen::new(|co| async move {
253 co.yield_(10).await;
254 co.yield_(20).await;
255});
256assert_eq!(gen.resume(), GeneratorState::Yielded(10));
257assert_eq!(gen.resume(), GeneratorState::Yielded(20));
258assert_eq!(gen.resume(), GeneratorState::Complete(()));
259```
260
261## Using the low-level API with function arguments
262
263This is just ordinary Rust, nothing special.
264
265```rust
266# use genawaiter::{rc::{Co, Gen}, GeneratorState};
267#
268async fn multiples_of(num: i32, co: Co<i32>) {
269 let mut cur = num;
270 loop {
271 co.yield_(cur).await;
272 cur += num;
273 }
274}
275
276let mut gen = Gen::new(|co| multiples_of(10, co));
277assert_eq!(gen.resume(), GeneratorState::Yielded(10));
278assert_eq!(gen.resume(), GeneratorState::Yielded(20));
279assert_eq!(gen.resume(), GeneratorState::Yielded(30));
280```
281*/
282
283pub use crate::rc::{engine::Co, generator::Gen};
284
285/// Creates a generator.
286///
287/// This macro takes one argument, which is the body of the generator. It should
288/// contain one or more calls to the [`yield_!`] macro.
289///
290/// # Examples
291///
292/// [_See the module-level docs for examples._](.)
293#[cfg(feature = "proc_macro")]
294pub use genawaiter_macro::rc_gen as gen;
295
296/// Turns a function into a producer, which can then be used to create a
297/// generator.
298///
299/// The body of the function should contain one or more [`yield_!`] expressions.
300///
301/// # Examples
302///
303/// [_See the module-level docs for examples._](.)
304#[cfg(feature = "proc_macro")]
305pub use genawaiter_proc_macro::rc_producer_fn as producer_fn;
306
307mod engine;
308mod generator;
309mod iterator;
310#[cfg(feature = "futures03")]
311mod stream;
312
313#[cfg(feature = "nightly")]
314#[cfg(test)]
315mod nightly_tests;
316
317#[cfg(test)]
318mod tests {
319 use crate::{
320 rc::{Co, Gen},
321 testing::DummyFuture,
322 GeneratorState,
323 };
324 use std::{
325 cell::{Cell, RefCell},
326 future::Future,
327 };
328
329 async fn simple_producer(co: Co<i32>) -> &'static str {
330 co.yield_(10).await;
331 "done"
332 }
333
334 #[test]
335 fn function() {
336 let mut gen = Gen::new(simple_producer);
337 assert_eq!(gen.resume(), GeneratorState::Yielded(10));
338 assert_eq!(gen.resume(), GeneratorState::Complete("done"));
339 }
340
341 #[test]
342 fn simple_closure() {
343 async fn gen(i: i32, co: Co<i32>) -> &'static str {
344 co.yield_(i * 2).await;
345 "done"
346 }
347
348 let mut gen = Gen::new(|co| gen(5, co));
349 assert_eq!(gen.resume(), GeneratorState::Yielded(10));
350 assert_eq!(gen.resume(), GeneratorState::Complete("done"));
351 }
352
353 #[test]
354 fn resume_args() {
355 async fn gen(resumes: &RefCell<Vec<&str>>, co: Co<i32, &'static str>) {
356 let resume_arg = co.yield_(10).await;
357 resumes.borrow_mut().push(resume_arg);
358 let resume_arg = co.yield_(20).await;
359 resumes.borrow_mut().push(resume_arg);
360 }
361
362 let resumes = RefCell::new(Vec::new());
363 let mut gen = Gen::new(|co| gen(&resumes, co));
364 assert_eq!(*resumes.borrow(), &[] as &[&str]);
365
366 assert_eq!(gen.resume_with("ignored"), GeneratorState::Yielded(10));
367 assert_eq!(*resumes.borrow(), &[] as &[&str]);
368
369 assert_eq!(gen.resume_with("abc"), GeneratorState::Yielded(20));
370 assert_eq!(*resumes.borrow(), &["abc"]);
371
372 assert_eq!(gen.resume_with("def"), GeneratorState::Complete(()));
373 assert_eq!(*resumes.borrow(), &["abc", "def"]);
374 }
375
376 #[test]
377 #[should_panic(expected = "non-async method")]
378 fn forbidden_await_helpful_message() {
379 async fn wrong(_: Co<i32>) {
380 DummyFuture.await;
381 }
382
383 let mut gen = Gen::new(wrong);
384 gen.resume();
385 }
386
387 #[test]
388 #[should_panic(expected = "Co::yield_")]
389 fn multiple_yield_helpful_message() {
390 async fn wrong(co: Co<i32>) {
391 let _ = co.yield_(10);
392 let _ = co.yield_(20);
393 }
394
395 let mut gen = Gen::new(wrong);
396 gen.resume();
397 }
398
399 #[test]
400 #[should_panic = "should have been dropped by now"]
401 fn escaped_co_helpful_message() {
402 async fn shenanigans(co: Co<i32>) -> Co<i32> {
403 co
404 }
405
406 let mut gen = Gen::new(shenanigans);
407 let escaped_co = match gen.resume() {
408 GeneratorState::Yielded(_) => panic!(),
409 GeneratorState::Complete(co) => co,
410 };
411 let _ = escaped_co.yield_(10);
412 }
413
414 /// This tests in a roundabout way that the `Gen` object can be moved. This
415 /// should happen without moving the allocations inside so we don't
416 /// segfault.
417 #[test]
418 fn gen_is_movable() {
419 #[inline(never)]
420 async fn produce(addrs: &mut Vec<*const i32>, co: Co<i32>) -> &'static str {
421 let sentinel: Cell<i32> = Cell::new(0x8001);
422 // If the future state moved, this reference would become invalid, and
423 // hilarity would ensue.
424 let sentinel_ref: &Cell<i32> = &sentinel;
425
426 // Test a few times that `sentinel` and `sentinel_ref` point to the same
427 // data.
428
429 assert_eq!(sentinel.get(), 0x8001);
430 sentinel_ref.set(0x8002);
431 assert_eq!(sentinel.get(), 0x8002);
432 addrs.push(sentinel.as_ptr());
433
434 co.yield_(10).await;
435
436 assert_eq!(sentinel.get(), 0x8002);
437 sentinel_ref.set(0x8003);
438 assert_eq!(sentinel.get(), 0x8003);
439 addrs.push(sentinel.as_ptr());
440
441 co.yield_(20).await;
442
443 assert_eq!(sentinel.get(), 0x8003);
444 sentinel_ref.set(0x8004);
445 assert_eq!(sentinel.get(), 0x8004);
446 addrs.push(sentinel.as_ptr());
447
448 "done"
449 }
450
451 /// Create a generator, resume it once (so `sentinel_ref` gets
452 /// initialized), and then move it out of the function.
453 fn create_generator(
454 addrs: &mut Vec<*const i32>,
455 ) -> Gen<i32, (), impl Future<Output = &'static str> + '_> {
456 let mut gen = Gen::new(move |co| produce(addrs, co));
457 assert_eq!(gen.resume(), GeneratorState::Yielded(10));
458 gen
459 }
460
461 let mut addrs = Vec::new();
462 let mut gen = create_generator(&mut addrs);
463
464 assert_eq!(gen.resume(), GeneratorState::Yielded(20));
465 assert_eq!(gen.resume(), GeneratorState::Complete("done"));
466 drop(gen);
467
468 assert!(addrs.iter().all(|&p| p == addrs[0]));
469 }
470}