genawaiter/stack/mod.rs
1/*!
2This module implements a generator which doesn't allocate.
3
4You can create a basic generator with [`let_gen!`] and [`yield_!`].
5
6[`let_gen!`]: macro.let_gen.html
7
8```rust
9# #[cfg(feature = "proc_macro")]
10# fn feature_gate() {
11# use genawaiter::{stack::let_gen, yield_};
12#
13let_gen!(my_generator, {
14 yield_!(10);
15});
16# my_generator.resume();
17# }
18```
19
20If you don't like macros, you can use the low-level API directly, though note that this
21requires you to trade away safety.
22
23```rust
24# use genawaiter::stack::{Co, Gen, Shelf};
25#
26async fn my_producer(co: Co<'_, u8>) {
27 co.yield_(10).await;
28}
29let mut shelf = Shelf::new();
30let mut my_generator = unsafe { Gen::new(&mut shelf, my_producer) };
31# my_generator.resume();
32```
33
34# Examples
35
36## Using `Iterator`
37
38Generators implement `Iterator`, so you can use them in a for loop:
39
40```rust
41# #[cfg(feature = "proc_macro")]
42# fn feature_gate() {
43use genawaiter::{stack::let_gen, yield_};
44
45let_gen!(odds_under_ten, {
46 let mut n = 1;
47 while n < 10 {
48 yield_!(n);
49 n += 2;
50 }
51});
52
53# let mut test = Vec::new();
54for num in odds_under_ten {
55 println!("{}", num);
56 # test.push(num);
57}
58# assert_eq!(test, [1, 3, 5, 7, 9]);
59# }
60```
61
62## Collecting into a `Vec`
63
64```rust
65# #[cfg(feature = "proc_macro")]
66# fn feature_gate() {
67# use genawaiter::{stack::let_gen, yield_};
68#
69# let_gen!(odds_under_ten, {
70# for n in (1..).step_by(2).take_while(|&n| n < 10) { yield_!(n); }
71# });
72#
73let xs: Vec<_> = odds_under_ten.into_iter().collect();
74assert_eq!(xs, [1, 3, 5, 7, 9]);
75# }
76```
77
78## A generator is a closure
79
80Like any closure, you can capture values from outer scopes.
81
82```rust
83# #[cfg(feature = "proc_macro")]
84# fn feature_gate() {
85# use genawaiter::{stack::let_gen, yield_, GeneratorState};
86#
87let two = 2;
88let_gen!(multiply, {
89 yield_!(10 * two);
90});
91assert_eq!(multiply.resume(), GeneratorState::Yielded(20));
92# }
93```
94
95## Using `resume()`
96
97```rust
98# #[cfg(feature = "proc_macro")]
99# fn feature_gate() {
100# use genawaiter::{stack::let_gen, yield_, GeneratorState};
101#
102# let_gen!(odds_under_ten, {
103# for n in (1..).step_by(2).take_while(|&n| n < 10) { yield_!(n); }
104# });
105#
106assert_eq!(odds_under_ten.resume(), GeneratorState::Yielded(1));
107assert_eq!(odds_under_ten.resume(), GeneratorState::Yielded(3));
108assert_eq!(odds_under_ten.resume(), GeneratorState::Yielded(5));
109assert_eq!(odds_under_ten.resume(), GeneratorState::Yielded(7));
110assert_eq!(odds_under_ten.resume(), GeneratorState::Yielded(9));
111assert_eq!(odds_under_ten.resume(), GeneratorState::Complete(()));
112# }
113```
114
115## Passing resume arguments
116
117You can pass values into the generator.
118
119Note that the first resume argument will be lost. This is because at the time the first
120value is sent, there is no future being awaited inside the generator, so there is no
121place the value could go where the generator could observe it.
122
123```rust
124# #[cfg(feature = "proc_macro")]
125# fn feature_gate() {
126# use genawaiter::{stack::let_gen, yield_};
127#
128let_gen!(check_numbers, {
129 let num = yield_!(());
130 assert_eq!(num, 1);
131
132 let num = yield_!(());
133 assert_eq!(num, 2);
134});
135
136check_numbers.resume_with(0);
137check_numbers.resume_with(1);
138check_numbers.resume_with(2);
139# }
140```
141
142## Returning a completion value
143
144You can return a completion value with a different type than the values that are
145yielded.
146
147```rust
148# #[cfg(feature = "proc_macro")]
149# fn feature_gate() {
150# use genawaiter::{stack::let_gen, yield_, GeneratorState};
151#
152let_gen!(numbers_then_string, {
153 yield_!(10);
154 yield_!(20);
155 "done!"
156});
157
158assert_eq!(numbers_then_string.resume(), GeneratorState::Yielded(10));
159assert_eq!(numbers_then_string.resume(), GeneratorState::Yielded(20));
160assert_eq!(numbers_then_string.resume(), GeneratorState::Complete("done!"));
161# }
162```
163
164## Defining a reusable producer function
165
166```rust
167# #[cfg(feature = "proc_macro")]
168# fn feature_gate() {
169# use genawaiter::{stack::{let_gen_using, producer_fn}, yield_, GeneratorState};
170#
171#[producer_fn(u8)]
172async fn produce() {
173 yield_!(10);
174}
175
176let_gen_using!(gen, produce);
177assert_eq!(gen.resume(), GeneratorState::Yielded(10));
178# }
179```
180
181## Using the low-level API
182
183You can define an `async fn` directly, instead of relying on the `gen!` or `producer!`
184macros.
185
186```rust
187use genawaiter::stack::{let_gen_using, Co};
188
189async fn producer(co: Co<'_, i32>) {
190 let mut n = 1;
191 while n < 10 {
192 co.yield_(n).await;
193 n += 2;
194 }
195}
196
197let_gen_using!(odds_under_ten, producer);
198let result: Vec<_> = odds_under_ten.into_iter().collect();
199assert_eq!(result, [1, 3, 5, 7, 9]);
200```
201
202## Using the low-level API with an async closure (nightly Rust only)
203
204```ignore
205# use genawaiter::{stack::let_gen_using, GeneratorState};
206#
207let_gen_using!(gen, async move |co| {
208 co.yield_(10).await;
209 co.yield_(20).await;
210});
211assert_eq!(gen.resume(), GeneratorState::Yielded(10));
212assert_eq!(gen.resume(), GeneratorState::Yielded(20));
213assert_eq!(gen.resume(), GeneratorState::Complete(()));
214```
215
216## Using the low-level API with an async <del>closure</del> faux·sure (for stable Rust)
217
218```
219# use genawaiter::{stack::let_gen_using, GeneratorState};
220#
221let_gen_using!(gen, |co| async move {
222 co.yield_(10).await;
223 co.yield_(20).await;
224});
225assert_eq!(gen.resume(), GeneratorState::Yielded(10));
226assert_eq!(gen.resume(), GeneratorState::Yielded(20));
227assert_eq!(gen.resume(), GeneratorState::Complete(()));
228```
229
230## Using the low-level API with function arguments
231
232This is just ordinary Rust, nothing special.
233
234```rust
235# use genawaiter::{stack::{let_gen_using, Co}, GeneratorState};
236#
237async fn multiples_of(num: i32, co: Co<'_, i32>) {
238 let mut cur = num;
239 loop {
240 co.yield_(cur).await;
241 cur += num;
242 }
243}
244
245let_gen_using!(gen, |co| multiples_of(10, co));
246assert_eq!(gen.resume(), GeneratorState::Yielded(10));
247assert_eq!(gen.resume(), GeneratorState::Yielded(20));
248assert_eq!(gen.resume(), GeneratorState::Yielded(30));
249```
250*/
251
252pub use crate::stack::{
253 engine::Co,
254 generator::{Gen, Shelf},
255};
256
257/// Creates a generator.
258///
259/// The first argument is the name of the resulting variable.
260///
261/// ```ignore
262/// let_gen!(my_generator, { /* ... */ });
263/// // Think of this as the spiritual equivalent of:
264/// let mut my_generator = Gen::new(/* ... */);
265/// ```
266///
267/// The second argument is the body of the generator. It should contain one or
268/// more calls to the [`yield_!`] macro.
269///
270/// This macro is a shortcut for creating both a generator and its backing state
271/// (called a [`Shelf`](struct.Shelf.html)). If you (or your IDE) dislike
272/// macros, you can also do the bookkeeping by hand by using
273/// [`Gen::new`](struct.Gen.html#method.new), though note that this requires you
274/// to trade away safety.
275///
276/// # Examples
277///
278/// [_See the module-level docs for examples._](.)
279#[cfg(feature = "proc_macro")]
280pub use genawaiter_macro::stack_let_gen as let_gen;
281
282/// Creates a generator using a producer defined elsewhere.
283///
284/// The first argument is the name of the resulting variable.
285///
286/// ```ignore
287/// let_gen!(my_generator, { /* ... */ });
288/// // Think of this as the spiritual equivalent of:
289/// let mut my_generator = Gen::new(/* ... */);
290/// ```
291///
292/// The second line is the producer that will be used. It can be one of these
293/// two things:
294///
295/// 1. The result of [`stack_producer!`] or [`stack_producer_fn!`]
296///
297/// [`stack_producer_fn!`]: attr.producer_fn.html
298///
299/// 2. A function with this type:
300///
301/// ```ignore
302/// async fn producer(co: Co<'_, Yield, Resume>) -> Completion { /* ... */ }
303/// // which is equivalent to:
304/// fn producer(co: Co<'_, Yield, Resume>) -> impl Future<Output = Completion> { /* ... */ }
305/// ```
306///
307/// This macro is a shortcut for creating both a generator and its backing state
308/// (called a [`Shelf`](struct.Shelf.html)). If you (or your IDE) dislike
309/// macros, you can also do the bookkeeping by hand by using
310/// [`Gen::new`](struct.Gen.html#method.new), though note that this requires you
311/// to trade away safety.
312///
313/// # Examples
314///
315/// [_See the module-level docs for examples._](.)
316pub use genawaiter_macro::stack_let_gen_using as let_gen_using;
317
318/// Turns a function into a producer, which can then be used to create a
319/// generator.
320///
321/// The body of the function should contain one or more [`yield_!`] expressions.
322///
323/// # Examples
324///
325/// [_See the module-level docs for examples._](.)
326#[cfg(feature = "proc_macro")]
327pub use genawaiter_proc_macro::stack_producer_fn as producer_fn;
328
329#[macro_use]
330mod macros;
331mod engine;
332mod generator;
333mod iterator;
334#[cfg(feature = "futures03")]
335mod stream;
336
337#[cfg(feature = "nightly")]
338#[cfg(test)]
339mod nightly_tests;
340
341#[cfg(test)]
342mod tests {
343 use crate::{
344 stack::{let_gen_using, Co},
345 testing::DummyFuture,
346 GeneratorState,
347 };
348 use std::{
349 cell::RefCell,
350 sync::{
351 atomic::{AtomicBool, Ordering},
352 Arc,
353 },
354 };
355
356 async fn simple_producer(co: Co<'_, i32>) -> &'static str {
357 co.yield_(10).await;
358 "done"
359 }
360
361 #[test]
362 fn function() {
363 let_gen_using!(gen, simple_producer);
364 assert_eq!(gen.resume(), GeneratorState::Yielded(10));
365 assert_eq!(gen.resume(), GeneratorState::Complete("done"));
366 }
367
368 #[test]
369 fn simple_closure() {
370 async fn gen(i: i32, co: Co<'_, i32>) -> &'static str {
371 co.yield_(i * 2).await;
372 "done"
373 }
374
375 let_gen_using!(gen, |co| gen(5, co));
376 assert_eq!(gen.resume(), GeneratorState::Yielded(10));
377 assert_eq!(gen.resume(), GeneratorState::Complete("done"));
378 }
379
380 #[test]
381 fn resume_args() {
382 async fn gen(resumes: &RefCell<Vec<&str>>, co: Co<'_, i32, &'static str>) {
383 let resume_arg = co.yield_(10).await;
384 resumes.borrow_mut().push(resume_arg);
385 let resume_arg = co.yield_(20).await;
386 resumes.borrow_mut().push(resume_arg);
387 }
388
389 let resumes = RefCell::new(Vec::new());
390 let_gen_using!(gen, |co| gen(&resumes, co));
391 assert_eq!(*resumes.borrow(), &[] as &[&str]);
392
393 assert_eq!(gen.resume_with("ignored"), GeneratorState::Yielded(10));
394 assert_eq!(*resumes.borrow(), &[] as &[&str]);
395
396 assert_eq!(gen.resume_with("abc"), GeneratorState::Yielded(20));
397 assert_eq!(*resumes.borrow(), &["abc"]);
398
399 assert_eq!(gen.resume_with("def"), GeneratorState::Complete(()));
400 assert_eq!(*resumes.borrow(), &["abc", "def"]);
401 }
402
403 #[test]
404 #[should_panic(expected = "non-async method")]
405 fn forbidden_await_helpful_message() {
406 async fn wrong(_: Co<'_, i32>) {
407 DummyFuture.await;
408 }
409
410 let_gen_using!(gen, wrong);
411 gen.resume();
412 }
413
414 #[test]
415 #[should_panic(expected = "Co::yield_")]
416 fn multiple_yield_helpful_message() {
417 async fn wrong(co: Co<'_, i32>) {
418 let _ = co.yield_(10);
419 let _ = co.yield_(20);
420 }
421
422 let_gen_using!(gen, wrong);
423 gen.resume();
424 }
425
426 #[test]
427 #[should_panic = "should have been dropped by now"]
428 fn escaped_co_helpful_message() {
429 async fn shenanigans(co: Co<'_, i32>) -> Co<'_, i32> {
430 co
431 }
432
433 let_gen_using!(gen, shenanigans);
434 let escaped_co = match gen.resume() {
435 GeneratorState::Yielded(_) => panic!(),
436 GeneratorState::Complete(co) => co,
437 };
438 let _ = escaped_co.yield_(10);
439 }
440
441 /// Test the unsafe `Gen::drop` implementation.
442 #[test]
443 fn test_gen_drop() {
444 struct SetFlagOnDrop(Arc<AtomicBool>);
445
446 impl Drop for SetFlagOnDrop {
447 fn drop(&mut self) {
448 self.0.store(true, Ordering::SeqCst);
449 }
450 }
451
452 let flag = Arc::new(AtomicBool::new(false));
453 {
454 let capture_the_flag = flag.clone();
455 let_gen_using!(gen, |co| {
456 async move {
457 let _set_on_drop = SetFlagOnDrop(capture_the_flag);
458 co.yield_(10).await;
459 // We will never make it this far.
460 unreachable!();
461 }
462 });
463 assert_eq!(gen.resume(), GeneratorState::Yielded(10));
464 // `gen` is only a reference to the generator, and dropping a reference has
465 // no effect. The underlying generator is hidden behind macro hygiene and so
466 // cannot be dropped early.
467 #[allow(clippy::drop_ref)]
468 drop(gen);
469 assert_eq!(flag.load(Ordering::SeqCst), false);
470 }
471 // After the block above ends, the generator goes out of scope and is dropped,
472 // which drops the incomplete future, which drops `_set_on_drop`, which sets the
473 // flag.
474 assert_eq!(flag.load(Ordering::SeqCst), true);
475 }
476}
477
478#[allow(dead_code)]
479mod doctests {
480 /**
481 Make sure `co` cannot escape to the `'static` lifetime.
482
483 ```compile_fail
484 use genawaiter::stack::{let_gen_using, Co};
485
486 async fn producer(co: Co<'static, i32>) {}
487
488 let_gen_using!(gen, producer);
489 ```
490 */
491 fn co_is_not_static() {}
492
493 /**
494 This test is exactly the same as above, but doesn't trigger the failure.
495
496 ```
497 use genawaiter::stack::{let_gen_using, Co};
498
499 async fn producer(co: Co<'_, i32>) {}
500
501 let_gen_using!(gen, producer);
502 ```
503 */
504 fn co_is_not_static_baseline() {}
505}
506
507#[allow(dead_code)]
508#[cfg(feature = "proc_macro")]
509mod doc_compile_fail {
510 /**
511 Make sure `co` cannot be used as argument by user.
512
513 ```compile_fail
514 use genawaiter::{stack::{producer_fn, Co}, yield_};
515
516 #[producer_fn(u8)]
517 async fn odds(co: Co<'_, u8>) {
518 yield_!(10);
519 }
520 ```
521 */
522 fn with_args_compile_fail() {}
523
524 /**
525 This test is exactly the same as above, except it passes.
526
527 ```rust
528 use genawaiter::{stack::{producer_fn, Co}, yield_};
529
530 #[producer_fn(u8)]
531 async fn odds() {
532 yield_!(10);
533 }
534 ```
535 */
536 fn with_args_compile_fail_baseline() {}
537}