| 
 | 1 | +# `generators`  | 
 | 2 | + | 
 | 3 | +The tracking issue for this feature is: [#43122]  | 
 | 4 | + | 
 | 5 | +[#34511]: https://github.com/rust-lang/rust/issues/43122  | 
 | 6 | + | 
 | 7 | +------------------------  | 
 | 8 | + | 
 | 9 | +The `generators` feature gate in Rust allows you to define generator or  | 
 | 10 | +coroutine literals. A generator is a "resumable function" that syntactically  | 
 | 11 | +resembles a closure but compiles to much different semantics in the compiler  | 
 | 12 | +itself. The primary feature of a generator is that it can be suspended during  | 
 | 13 | +execution to be resumed at a later date. Generators use the `yield` keyword to  | 
 | 14 | +"return", and then the caller can `resume` a generator to resume execution just  | 
 | 15 | +after the `yield` keyword.  | 
 | 16 | + | 
 | 17 | +Generators are an extra-unstable feature in the compiler right now. Added in  | 
 | 18 | +[RFC 2033] they're mostly intended right now as a information/constraint  | 
 | 19 | +gathering phase. The intent is that experimentation can happen on the nightly  | 
 | 20 | +compiler before actual stabilization. A further RFC will be required to  | 
 | 21 | +stabilize generators/coroutines and will likely contain at least a few small  | 
 | 22 | +tweaks to the overall design.  | 
 | 23 | + | 
 | 24 | +[RFC 2033]: https://github.com/rust-lang/rfcs/pull/2033  | 
 | 25 | + | 
 | 26 | +A syntactical example of a generator is:  | 
 | 27 | + | 
 | 28 | +```rust  | 
 | 29 | +#![feature(generators, generator_trait)]  | 
 | 30 | + | 
 | 31 | +use std::ops::{Generator, GeneratorState};  | 
 | 32 | + | 
 | 33 | +fn main() {  | 
 | 34 | +    let mut generator = || {  | 
 | 35 | +        yield 1;  | 
 | 36 | +        return "foo"  | 
 | 37 | +    };  | 
 | 38 | + | 
 | 39 | +    match generator.resume() {  | 
 | 40 | +        GeneratorState::Yielded(1) => {}  | 
 | 41 | +        _ => panic!("unexpected value from resume"),  | 
 | 42 | +    }  | 
 | 43 | +    match generator.resume() {  | 
 | 44 | +        GeneratorState::Complete("foo") => {}  | 
 | 45 | +        _ => panic!("unexpected value from resume"),  | 
 | 46 | +    }  | 
 | 47 | +}  | 
 | 48 | +```  | 
 | 49 | + | 
 | 50 | +Generators are closure-like literals which can contain a `yield` statement. The  | 
 | 51 | +`yield` statement takes an optional expression of a value to yield out of the  | 
 | 52 | +generator. All generator literals implement the `Generator` trait in the  | 
 | 53 | +`std::ops` module. The `Generator` trait has one main method, `resume`, which  | 
 | 54 | +resumes execution of the generator at the previous suspension point.  | 
 | 55 | + | 
 | 56 | +An example of the control flow of generators is that the following example  | 
 | 57 | +prints all numbers in order:  | 
 | 58 | + | 
 | 59 | +```rust  | 
 | 60 | +#![feature(generators, generator_trait)]  | 
 | 61 | + | 
 | 62 | +use std::ops::Generator;  | 
 | 63 | + | 
 | 64 | +fn main() {  | 
 | 65 | +    let mut generator = || {  | 
 | 66 | +        println!("2");  | 
 | 67 | +        yield;  | 
 | 68 | +        println!("4");  | 
 | 69 | +    };  | 
 | 70 | + | 
 | 71 | +    println!("1");  | 
 | 72 | +    generator.resume();  | 
 | 73 | +    println!("3");  | 
 | 74 | +    generator.resume();  | 
 | 75 | +    println!("5");  | 
 | 76 | +}  | 
 | 77 | +```  | 
 | 78 | + | 
 | 79 | +At this time the main intended use case of generators is an implementation  | 
 | 80 | +primitive for async/await syntax, but generators will likely be extended to  | 
 | 81 | +ergonomic implementations of iterators and other primitives in the future.  | 
 | 82 | +Feedback on the design and usage is always appreciated!  | 
 | 83 | + | 
 | 84 | +### The `Generator` trait  | 
 | 85 | + | 
 | 86 | +The `Generator` trait in `std::ops` currently looks like:  | 
 | 87 | + | 
 | 88 | +```  | 
 | 89 | +# #![feature(generator_trait)]  | 
 | 90 | +# use std::ops::GeneratorState;  | 
 | 91 | +
  | 
 | 92 | +pub trait Generator {  | 
 | 93 | +    type Yield;  | 
 | 94 | +    type Return;  | 
 | 95 | +    fn resume(&mut self) -> GeneratorState<Self::Yield, Self::Return>;  | 
 | 96 | +}  | 
 | 97 | +```  | 
 | 98 | + | 
 | 99 | +The `Generator::Yield` type is the type of values that can be yielded with the  | 
 | 100 | +`yield` statement. The `Generator::Return` type is the returned type of the  | 
 | 101 | +generator. This is typically the last expression in a generator's definition or  | 
 | 102 | +any value passed to `return` in a generator. The `resume` function is the entry  | 
 | 103 | +point for executing the `Generator` itself.  | 
 | 104 | + | 
 | 105 | +The return value of `resume`, `GeneratorState`, looks like:  | 
 | 106 | + | 
 | 107 | +```  | 
 | 108 | +pub enum GeneratorState<Y, R> {  | 
 | 109 | +    Yielded(Y),  | 
 | 110 | +    Complete(R),  | 
 | 111 | +}  | 
 | 112 | +```  | 
 | 113 | + | 
 | 114 | +The `Yielded` variant indicates that the generator can later be resumed. This  | 
 | 115 | +corresponds to a `yield` point in a generator. The `Complete` variant indicates  | 
 | 116 | +that the generator is complete and cannot be resumed again. Calling `resume`  | 
 | 117 | +after a generator has returned `Complete` will likely result in a panic of the  | 
 | 118 | +program.  | 
 | 119 | + | 
 | 120 | +### Closure-like semantics  | 
 | 121 | + | 
 | 122 | +The closure-like syntax for generators alludes to the fact that they also have  | 
 | 123 | +closure-like semantics. Namely:  | 
 | 124 | + | 
 | 125 | +* When created, a generator executes no code. A closure literal does not  | 
 | 126 | +  actually execute any of the closure's code on construction, and similarly a  | 
 | 127 | +  generator literal does not execute any code inside the generator when  | 
 | 128 | +  constructed.  | 
 | 129 | + | 
 | 130 | +* Generators can capture outer variables by reference or by move, and this can  | 
 | 131 | +  be tweaked with the `move` keyword at the beginning of the closure. Like  | 
 | 132 | +  closures all generators will have an implicit environment which is inferred by  | 
 | 133 | +  the compiler. Outer variables can be moved into a generator for use as the  | 
 | 134 | +  generator progresses.  | 
 | 135 | + | 
 | 136 | +* Generator literals produce a value with a unique type which implements the  | 
 | 137 | +  `std::ops::Generator` trait. This allows actual execution of the generator  | 
 | 138 | +  through the `Generator::resume` method as well as also naming it in return  | 
 | 139 | +  types and such.  | 
 | 140 | + | 
 | 141 | +* Traits like `Send` and `Sync` are automatically implemented for a `Generator`  | 
 | 142 | +  depending on the captured variables of the environment. Unlike closures though  | 
 | 143 | +  generators also depend on variables live across suspension points. This means  | 
 | 144 | +  that although the ambient environment may be `Send` or `Sync`, the generator  | 
 | 145 | +  itself may not be due to internal variables live across `yield` points being  | 
 | 146 | +  not-`Send` or not-`Sync`. Note, though, that generators, like closures, do  | 
 | 147 | +  not implement traits like `Copy` or `Clone` automatically.  | 
 | 148 | + | 
 | 149 | +* Whenever a generator is dropped it will drop all captured environment  | 
 | 150 | +  variables.  | 
 | 151 | + | 
 | 152 | +Note that unlike closures generators at this time cannot take any arguments.  | 
 | 153 | +That is, generators must always look like `|| { ... }`. This restriction may be  | 
 | 154 | +lifted at a future date, the design is ongoing!  | 
 | 155 | + | 
 | 156 | +### Generators as state machines  | 
 | 157 | + | 
 | 158 | +In the compiler generators are currently compiled as state machines. Each  | 
 | 159 | +`yield` expression will correspond to a different state that stores all live  | 
 | 160 | +variables over that suspension point. Resumption of a generator will dispatch on  | 
 | 161 | +the current state and then execute internally until a `yield` is reached, at  | 
 | 162 | +which point all state is saved off in the generator and a value is returned.  | 
 | 163 | + | 
 | 164 | +Let's take a look at an example to see what's going on here:  | 
 | 165 | + | 
 | 166 | +```rust  | 
 | 167 | +#![feature(generators, generator_trait)]  | 
 | 168 | + | 
 | 169 | +use std::ops::Generator;  | 
 | 170 | + | 
 | 171 | +fn main() {  | 
 | 172 | +    let ret = "foo";  | 
 | 173 | +    let mut generator = move || {  | 
 | 174 | +        yield 1;  | 
 | 175 | +        return ret  | 
 | 176 | +    };  | 
 | 177 | + | 
 | 178 | +    generator.resume();  | 
 | 179 | +    generator.resume();  | 
 | 180 | +}  | 
 | 181 | +```  | 
 | 182 | + | 
 | 183 | +This generator literal will compile down to something similar to:  | 
 | 184 | + | 
 | 185 | +```rust  | 
 | 186 | +#![feature(generators, generator_trait)]  | 
 | 187 | + | 
 | 188 | +use std::ops::{Generator, GeneratorState};  | 
 | 189 | + | 
 | 190 | +fn main() {  | 
 | 191 | +    let ret = "foo";  | 
 | 192 | +    let mut generator = {  | 
 | 193 | +        enum __Generator {  | 
 | 194 | +            Start(&'static str),  | 
 | 195 | +            Yield1(&'static str),  | 
 | 196 | +            Done,  | 
 | 197 | +        }  | 
 | 198 | + | 
 | 199 | +        impl Generator for __Generator {  | 
 | 200 | +            type Yield = i32;  | 
 | 201 | +            type Return = &'static str;  | 
 | 202 | + | 
 | 203 | +            fn resume(&mut self) -> GeneratorState<i32, &'static str> {  | 
 | 204 | +                use std::mem;  | 
 | 205 | +                match mem::replace(self, __Generator::Done) {  | 
 | 206 | +                    __Generator::Start(s) => {  | 
 | 207 | +                        *self = __Generator::Yield1(s);  | 
 | 208 | +                        GeneratorState::Yielded(1)  | 
 | 209 | +                    }  | 
 | 210 | + | 
 | 211 | +                    __Generator::Yield1(s) => {  | 
 | 212 | +                        *self = __Generator::Done;  | 
 | 213 | +                        GeneratorState::Complete(s)  | 
 | 214 | +                    }  | 
 | 215 | + | 
 | 216 | +                    __Generator::Done => {  | 
 | 217 | +                        panic!("generator resumed after completion")  | 
 | 218 | +                    }  | 
 | 219 | +                }  | 
 | 220 | +            }  | 
 | 221 | +        }  | 
 | 222 | + | 
 | 223 | +        __Generator::Start(ret)  | 
 | 224 | +    };  | 
 | 225 | + | 
 | 226 | +    generator.resume();  | 
 | 227 | +    generator.resume();  | 
 | 228 | +}  | 
 | 229 | +```  | 
 | 230 | + | 
 | 231 | +Notably here we can see that the compiler is generating a fresh type,  | 
 | 232 | +`__Generator` in this case. This type has a number of states (represented here  | 
 | 233 | +as an `enum`) corresponding to each of the conceptual states of the generator.  | 
 | 234 | +At the beginning we're closing over our outer variable `foo` and then that  | 
 | 235 | +variable is also live over the `yield` point, so it's stored in both states.  | 
 | 236 | + | 
 | 237 | +When the generator starts it'll immediately yield 1, but it saves off its state  | 
 | 238 | +just before it does so indicating that it has reached the yield point. Upon  | 
 | 239 | +resuming again we'll execute the `return ret` which returns the `Complete`  | 
 | 240 | +state.  | 
 | 241 | + | 
 | 242 | +Here we can also note that the `Done` state, if resumed, panics immediately as  | 
 | 243 | +it's invalid to resume a completed generator. It's also worth noting that this  | 
 | 244 | +is just a rough desugaring, not a normative specification for what the compiler  | 
 | 245 | +does.  | 
0 commit comments