genawaiter/lib.rs
1/*!
2This crate implements generators for Rust. Generators are a feature common across many
3programming language. They let you yield a sequence of values from a function. A few
4common use cases are:
5
6- Easily building iterators.
7- Avoiding allocating a list for a function which returns multiple values.
8
9Rust has this feature too, but it is currently unstable (and thus nightly-only). But
10with this crate, you can use them on stable Rust!
11
12# Features
13
14This crate has these features:
15
16- `futures03` (disabled by default) – Implements `Stream` for all generator types.
17 Adds a dependency on `futures-core`.
18- `proc_macro` (enabled by default) – Adds support for macros, and adds various
19 compile-time dependencies.
20
21# Choose your guarantees
22
23This crate supplies three concrete implementations of generators:
24
251. [`genawaiter::stack`](stack) – Allocation-free. You should prefer this when possible.
26
272. [`genawaiter::rc`](rc) – This allocates.
28
293. [`genawaiter::sync`](sync) – This allocates, and can be shared between threads.
30
31 [unus]: https://github.com/whatisaphone/genawaiter/blob/4a2b185/src/waker.rs#L9
32 [duo]: https://github.com/whatisaphone/genawaiter/blob/4a2b185/src/rc/engine.rs#L26
33
34Here are the differences in table form:
35
36| | [`stack::Gen`] | [`rc::Gen`] | [`sync::Gen`] |
37|---------------------------------------|----------------|-------------|---------------|
38| Allocations per generator | 0 | 2 | 2 |
39| Generator can be moved after created | no | yes | yes |
40| Thread-safe | no | no | yes |
41
42# Creating a generator
43
44Once you've chosen how and whether to allocate (see previous section), you can create a
45generator using a macro from the `gen` family:
46
47- [`stack::let_gen!`](stack/macro.let_gen.html)
48- [`rc::gen!`](rc/macro.gen.html)
49- [`sync::gen!`](sync/macro.gen.html)
50
51```rust
52# #[cfg(feature = "proc_macro")]
53# fn feature_gate() {
54# use genawaiter::{sync::gen, yield_};
55#
56let count_to_ten = gen!({
57 for n in 0..10 {
58 yield_!(n);
59 }
60});
61
62# let result: Vec<_> = count_to_ten.into_iter().collect();
63# assert_eq!(result, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
64# }
65```
66
67To re-use logic between multiple generators, you can use a macro from the `producer`
68family, and then pass the producer to `Gen::new`.
69
70- [`stack_producer!`] and [`let_gen_using!`](stack/macro.let_gen_using.html)
71- [`rc_producer!`] and [`Gen::new`](rc::Gen::new)
72- [`sync_producer!`] and [`Gen::new`](sync::Gen::new)
73
74```rust
75# #[cfg(feature = "proc_macro")]
76# fn feature_gate() {
77# use genawaiter::{sync::Gen, sync_producer as producer, yield_};
78#
79let count_producer = producer!({
80 for n in 0..10 {
81 yield_!(n);
82 }
83});
84
85let count_to_ten = Gen::new(count_producer);
86
87# let result: Vec<_> = count_to_ten.into_iter().collect();
88# assert_eq!(result, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
89# }
90```
91
92If neither of these offers enough control for you, you can always skip the macros and
93use the low-level API directly:
94
95```rust
96# use genawaiter::sync::{Co, Gen};
97#
98let count_to_ten = Gen::new(|co| async move {
99 for n in 0..10 {
100 co.yield_(n).await;
101 }
102});
103
104# let result: Vec<_> = count_to_ten.into_iter().collect();
105# assert_eq!(result, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
106```
107
108# A tale of three types
109
110A generator can control the flow of up to three types of data:
111
112- **Yield** – Each time a generator suspends execution, it can produce a value.
113- **Resume** – Each time a generator is resumed, a value can be passed in.
114- **Completion** – When a generator completes, it can produce one final value.
115
116## Yield
117
118Values can be yielded from the generator by calling `yield_`, and immediately awaiting
119the future it returns. You can get these values out of the generator in either of two
120ways:
121
122- Call `resume()` or `resume_with()`. The values will be returned in a
123 `GeneratorState::Yielded`.
124
125 ```rust
126 # #[cfg(feature = "proc_macro")]
127 # fn feature_gate() {
128 # use genawaiter::{sync::gen, yield_, GeneratorState};
129 #
130 let mut generator = gen!({
131 yield_!(10);
132 });
133 let ten = generator.resume();
134 assert_eq!(ten, GeneratorState::Yielded(10));
135 # }
136 ```
137
138- Treat it as an iterator. For this to work, both the resume and completion types must
139 be `()` .
140
141 ```rust
142 # #[cfg(feature = "proc_macro")]
143 # fn feature_gate() {
144 # use genawaiter::{sync::gen, yield_};
145 #
146 let generator = gen!({
147 yield_!(10);
148 });
149 let xs: Vec<_> = generator.into_iter().collect();
150 assert_eq!(xs, [10]);
151 # }
152 ```
153
154## Resume
155
156You can also send values back into the generator, by using `resume_with`. The generator
157receives them from the future returned by `yield_`.
158
159```rust
160# #[cfg(feature = "proc_macro")]
161# fn feature_gate() {
162# use genawaiter::{sync::gen, yield_};
163#
164let mut printer = gen!({
165 loop {
166 let string = yield_!(());
167 println!("{}", string);
168 }
169});
170printer.resume_with("hello");
171printer.resume_with("world");
172# }
173```
174
175## Completion
176
177A generator can produce one final value upon completion, by returning it from the
178function. The consumer will receive this value as a `GeneratorState::Complete`.
179
180```rust
181# #[cfg(feature = "proc_macro")]
182# fn feature_gate() {
183# use genawaiter::{sync::gen, yield_, GeneratorState};
184#
185let mut generator = gen!({
186 yield_!(10);
187 "done"
188});
189assert_eq!(generator.resume(), GeneratorState::Yielded(10));
190assert_eq!(generator.resume(), GeneratorState::Complete("done"));
191# }
192```
193
194# Async generators
195
196If you await other futures inside the generator, it becomes an _async generator_. It
197does not makes sense to treat an async generator as an `Iterable`, since you cannot
198`await` an `Iterable`. Instead, you can treat it as a `Stream`. This requires opting in
199to the dependency on `futures` with the `futures03` feature.
200
201```toml
202[dependencies]
203genawaiter = { version = "...", features = ["futures03"] }
204```
205
206```rust
207# #[cfg(all(feature = "proc_macro", feature = "futures03"))]
208# fn feature_gate() {
209# use futures::executor::block_on_stream;
210# use genawaiter::{sync::gen, yield_};
211#
212async fn async_one() -> i32 { 1 }
213async fn async_two() -> i32 { 2 }
214
215let gen = gen!({
216 let one = async_one().await;
217 yield_!(one);
218 let two = async_two().await;
219 yield_!(two);
220});
221let stream = block_on_stream(gen);
222let items: Vec<_> = stream.collect();
223assert_eq!(items, [1, 2]);
224# }
225```
226
227Async generators also provide a `async_resume` method for lower-level control. (This
228works even without the `futures03` feature.)
229
230```rust
231# #[cfg(feature = "proc_macro")]
232# async fn feature_gate() {
233# use genawaiter::{sync::gen, yield_, GeneratorState};
234# use std::task::Poll;
235#
236# let mut gen = gen!({
237# yield_!(10);
238# });
239#
240match gen.async_resume().await {
241 GeneratorState::Yielded(_) => {}
242 GeneratorState::Complete(_) => {}
243}
244# }
245```
246
247# Backported stdlib types
248
249This crate supplies [`Generator`](trait.Generator.html) and
250[`GeneratorState`](enum.GeneratorState.html). They are copy/pasted from the stdlib (with
251stability attributes removed) so they can be used on stable Rust. If/when real
252generators are stabilized, hopefully they would be drop-in replacements. Javascript
253developers might recognize this as a polyfill.
254
255There is also a [`Coroutine`](trait.Coroutine.html) trait, which does not come from the
256stdlib. A `Coroutine` is a generalization of a `Generator`. A `Generator` constrains the
257resume argument type to `()`, but in a `Coroutine` it can be anything.
258*/
259
260#![cfg_attr(feature = "nightly", feature(async_closure))]
261#![warn(future_incompatible, rust_2018_compatibility, rust_2018_idioms, unused)]
262#![warn(missing_docs, clippy::cargo, clippy::pedantic)]
263#![cfg_attr(feature = "strict", deny(warnings))]
264
265#[cfg(test)]
266extern crate self as genawaiter;
267
268pub use crate::ops::{Coroutine, Generator, GeneratorState};
269
270#[cfg(feature = "proc_macro")]
271use proc_macro_hack::proc_macro_hack;
272
273/// Creates a producer for use with [`sync::Gen`].
274///
275/// A producer can later be turned into a generator using
276/// [`Gen::new`](sync::Gen::new).
277///
278/// This macro takes one argument, which should be a block containing one or
279/// more calls to [`yield_!`].
280///
281/// # Example
282///
283/// ```rust
284/// use genawaiter::{sync::Gen, sync_producer as producer, yield_};
285///
286/// let my_producer = producer!({
287/// yield_!(10);
288/// });
289///
290/// let mut my_generator = Gen::new(my_producer);
291/// # my_generator.resume();
292/// ```
293#[cfg(feature = "proc_macro")]
294#[proc_macro_hack]
295pub use genawaiter_proc_macro::sync_producer;
296
297/// Creates a producer for use with [`rc::Gen`].
298///
299/// A producer can later be turned into a generator using
300/// [`Gen::new`](rc::Gen::new).
301///
302/// This macro takes one argument, which should be a block containing one or
303/// more calls to [`yield_!`].
304///
305/// # Example
306///
307/// ```rust
308/// use genawaiter::{rc::Gen, rc_producer as producer, yield_};
309///
310/// let my_producer = producer!({
311/// yield_!(10);
312/// });
313///
314/// let mut my_generator = Gen::new(my_producer);
315/// # my_generator.resume();
316/// ```
317#[cfg(feature = "proc_macro")]
318#[proc_macro_hack]
319pub use genawaiter_proc_macro::rc_producer;
320
321#[doc(hidden)] // This is not quite usable currently, so hide it for now.
322#[cfg(feature = "proc_macro")]
323#[proc_macro_hack]
324pub use genawaiter_proc_macro::stack_producer;
325
326mod core;
327mod ext;
328#[macro_use]
329mod macros;
330mod ops;
331pub mod rc;
332pub mod stack;
333pub mod sync;
334#[cfg(test)]
335mod testing;
336mod waker;