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}