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 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336
/*!
This crate implements generators for Rust. Generators are a feature common across many
programming language. They let you yield a sequence of values from a function. A few
common use cases are:
- Easily building iterators.
- Avoiding allocating a list for a function which returns multiple values.
Rust has this feature too, but it is currently unstable (and thus nightly-only). But
with this crate, you can use them on stable Rust!
# Features
This crate has these features:
- `futures03` (disabled by default) – Implements `Stream` for all generator types.
Adds a dependency on `futures-core`.
- `proc_macro` (enabled by default) – Adds support for macros, and adds various
compile-time dependencies.
# Choose your guarantees
This crate supplies three concrete implementations of generators:
1. [`genawaiter::stack`](stack) – Allocation-free. You should prefer this when possible.
2. [`genawaiter::rc`](rc) – This allocates.
3. [`genawaiter::sync`](sync) – This allocates, and can be shared between threads.
[unus]: https://github.com/whatisaphone/genawaiter/blob/4a2b185/src/waker.rs#L9
[duo]: https://github.com/whatisaphone/genawaiter/blob/4a2b185/src/rc/engine.rs#L26
Here are the differences in table form:
| | [`stack::Gen`] | [`rc::Gen`] | [`sync::Gen`] |
|---------------------------------------|----------------|-------------|---------------|
| Allocations per generator | 0 | 2 | 2 |
| Generator can be moved after created | no | yes | yes |
| Thread-safe | no | no | yes |
# Creating a generator
Once you've chosen how and whether to allocate (see previous section), you can create a
generator using a macro from the `gen` family:
- [`stack::let_gen!`](stack/macro.let_gen.html)
- [`rc::gen!`](rc/macro.gen.html)
- [`sync::gen!`](sync/macro.gen.html)
```rust
# #[cfg(feature = "proc_macro")]
# fn feature_gate() {
# use genawaiter::{sync::gen, yield_};
#
let count_to_ten = gen!({
for n in 0..10 {
yield_!(n);
}
});
# let result: Vec<_> = count_to_ten.into_iter().collect();
# assert_eq!(result, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
# }
```
To re-use logic between multiple generators, you can use a macro from the `producer`
family, and then pass the producer to `Gen::new`.
- [`stack_producer!`] and [`let_gen_using!`](stack/macro.let_gen_using.html)
- [`rc_producer!`] and [`Gen::new`](rc::Gen::new)
- [`sync_producer!`] and [`Gen::new`](sync::Gen::new)
```rust
# #[cfg(feature = "proc_macro")]
# fn feature_gate() {
# use genawaiter::{sync::Gen, sync_producer as producer, yield_};
#
let count_producer = producer!({
for n in 0..10 {
yield_!(n);
}
});
let count_to_ten = Gen::new(count_producer);
# let result: Vec<_> = count_to_ten.into_iter().collect();
# assert_eq!(result, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
# }
```
If neither of these offers enough control for you, you can always skip the macros and
use the low-level API directly:
```rust
# use genawaiter::sync::{Co, Gen};
#
let count_to_ten = Gen::new(|co| async move {
for n in 0..10 {
co.yield_(n).await;
}
});
# let result: Vec<_> = count_to_ten.into_iter().collect();
# assert_eq!(result, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
```
# A tale of three types
A generator can control the flow of up to three types of data:
- **Yield** – Each time a generator suspends execution, it can produce a value.
- **Resume** – Each time a generator is resumed, a value can be passed in.
- **Completion** – When a generator completes, it can produce one final value.
## Yield
Values can be yielded from the generator by calling `yield_`, and immediately awaiting
the future it returns. You can get these values out of the generator in either of two
ways:
- Call `resume()` or `resume_with()`. The values will be returned in a
`GeneratorState::Yielded`.
```rust
# #[cfg(feature = "proc_macro")]
# fn feature_gate() {
# use genawaiter::{sync::gen, yield_, GeneratorState};
#
let mut generator = gen!({
yield_!(10);
});
let ten = generator.resume();
assert_eq!(ten, GeneratorState::Yielded(10));
# }
```
- Treat it as an iterator. For this to work, both the resume and completion types must
be `()` .
```rust
# #[cfg(feature = "proc_macro")]
# fn feature_gate() {
# use genawaiter::{sync::gen, yield_};
#
let generator = gen!({
yield_!(10);
});
let xs: Vec<_> = generator.into_iter().collect();
assert_eq!(xs, [10]);
# }
```
## Resume
You can also send values back into the generator, by using `resume_with`. The generator
receives them from the future returned by `yield_`.
```rust
# #[cfg(feature = "proc_macro")]
# fn feature_gate() {
# use genawaiter::{sync::gen, yield_};
#
let mut printer = gen!({
loop {
let string = yield_!(());
println!("{}", string);
}
});
printer.resume_with("hello");
printer.resume_with("world");
# }
```
## Completion
A generator can produce one final value upon completion, by returning it from the
function. The consumer will receive this value as a `GeneratorState::Complete`.
```rust
# #[cfg(feature = "proc_macro")]
# fn feature_gate() {
# use genawaiter::{sync::gen, yield_, GeneratorState};
#
let mut generator = gen!({
yield_!(10);
"done"
});
assert_eq!(generator.resume(), GeneratorState::Yielded(10));
assert_eq!(generator.resume(), GeneratorState::Complete("done"));
# }
```
# Async generators
If you await other futures inside the generator, it becomes an _async generator_. It
does not makes sense to treat an async generator as an `Iterable`, since you cannot
`await` an `Iterable`. Instead, you can treat it as a `Stream`. This requires opting in
to the dependency on `futures` with the `futures03` feature.
```toml
[dependencies]
genawaiter = { version = "...", features = ["futures03"] }
```
```rust
# #[cfg(all(feature = "proc_macro", feature = "futures03"))]
# fn feature_gate() {
# use futures::executor::block_on_stream;
# use genawaiter::{sync::gen, yield_};
#
async fn async_one() -> i32 { 1 }
async fn async_two() -> i32 { 2 }
let gen = gen!({
let one = async_one().await;
yield_!(one);
let two = async_two().await;
yield_!(two);
});
let stream = block_on_stream(gen);
let items: Vec<_> = stream.collect();
assert_eq!(items, [1, 2]);
# }
```
Async generators also provide a `async_resume` method for lower-level control. (This
works even without the `futures03` feature.)
```rust
# #[cfg(feature = "proc_macro")]
# async fn feature_gate() {
# use genawaiter::{sync::gen, yield_, GeneratorState};
# use std::task::Poll;
#
# let mut gen = gen!({
# yield_!(10);
# });
#
match gen.async_resume().await {
GeneratorState::Yielded(_) => {}
GeneratorState::Complete(_) => {}
}
# }
```
# Backported stdlib types
This crate supplies [`Generator`](trait.Generator.html) and
[`GeneratorState`](enum.GeneratorState.html). They are copy/pasted from the stdlib (with
stability attributes removed) so they can be used on stable Rust. If/when real
generators are stabilized, hopefully they would be drop-in replacements. Javascript
developers might recognize this as a polyfill.
There is also a [`Coroutine`](trait.Coroutine.html) trait, which does not come from the
stdlib. A `Coroutine` is a generalization of a `Generator`. A `Generator` constrains the
resume argument type to `()`, but in a `Coroutine` it can be anything.
*/
#![cfg_attr(feature = "nightly", feature(async_closure))]
#![warn(future_incompatible, rust_2018_compatibility, rust_2018_idioms, unused)]
#![warn(missing_docs, clippy::cargo, clippy::pedantic)]
#![cfg_attr(feature = "strict", deny(warnings))]
#[cfg(test)]
extern crate self as genawaiter;
pub use crate::ops::{Coroutine, Generator, GeneratorState};
#[cfg(feature = "proc_macro")]
use proc_macro_hack::proc_macro_hack;
/// Creates a producer for use with [`sync::Gen`].
///
/// A producer can later be turned into a generator using
/// [`Gen::new`](sync::Gen::new).
///
/// This macro takes one argument, which should be a block containing one or
/// more calls to [`yield_!`].
///
/// # Example
///
/// ```rust
/// use genawaiter::{sync::Gen, sync_producer as producer, yield_};
///
/// let my_producer = producer!({
/// yield_!(10);
/// });
///
/// let mut my_generator = Gen::new(my_producer);
/// # my_generator.resume();
/// ```
#[cfg(feature = "proc_macro")]
#[proc_macro_hack]
pub use genawaiter_proc_macro::sync_producer;
/// Creates a producer for use with [`rc::Gen`].
///
/// A producer can later be turned into a generator using
/// [`Gen::new`](rc::Gen::new).
///
/// This macro takes one argument, which should be a block containing one or
/// more calls to [`yield_!`].
///
/// # Example
///
/// ```rust
/// use genawaiter::{rc::Gen, rc_producer as producer, yield_};
///
/// let my_producer = producer!({
/// yield_!(10);
/// });
///
/// let mut my_generator = Gen::new(my_producer);
/// # my_generator.resume();
/// ```
#[cfg(feature = "proc_macro")]
#[proc_macro_hack]
pub use genawaiter_proc_macro::rc_producer;
#[doc(hidden)] // This is not quite usable currently, so hide it for now.
#[cfg(feature = "proc_macro")]
#[proc_macro_hack]
pub use genawaiter_proc_macro::stack_producer;
mod core;
mod ext;
#[macro_use]
mod macros;
mod ops;
pub mod rc;
pub mod stack;
pub mod sync;
#[cfg(test)]
mod testing;
mod waker;