fixed some of the generator examples that were a bit unprecise

This commit is contained in:
Carl Fredrik Samson
2020-02-05 22:41:14 +01:00
parent ad11d123f6
commit 53529fa769
2 changed files with 26 additions and 17 deletions

View File

@@ -80,7 +80,7 @@ async/await as keywords (it can even be done using a macro).
2. No need for context switching and saving/restoring CPU state 2. No need for context switching and saving/restoring CPU state
3. No need to handle dynamic stack allocation 3. No need to handle dynamic stack allocation
4. Very memory efficient 4. Very memory efficient
5. Allowed for borrows across suspension points 5. Allows us to borrow across suspension points
The last point is in contrast to `Futures 1.0`. With async/await we can do this: The last point is in contrast to `Futures 1.0`. With async/await we can do this:
@@ -101,24 +101,30 @@ require any increased memory at all.
## How generators work ## How generators work
In Nightly Rust today you can use the `yield` keyword. Basically using this In Nightly Rust today you can use the `yield` keyword. Basically using this
keyword in a closure, converts it to a generator. A closure looking like this keyword in a closure, converts it to a generator. A closure could look like this
(I'm going to use the terminology that's currently in Rust): before we had a concept of `Pin`:
```rust,noplaypen,ignore ```rust,noplaypen,ignore
let a = 4; #![feature(generators, generator_trait)]
let b = move || { use std::ops::{Generator, GeneratorState};
fn main() {
let a: i32 = 4;
let mut gen = move || {
println!("Hello"); println!("Hello");
yield a * 2; yield a * 2;
println!("world!"); println!("world!");
}; };
if let GeneratorState::Yielded(n) = gen.resume() { if let GeneratorState::Yielded(n) = gen.resume() {
println!("Got value {}", n); println!("Got value {}", n);
} }
if let GeneratorState::Complete(()) = gen.resume() { if let GeneratorState::Complete(()) = gen.resume() {
() ()
}; };
}
``` ```
Early on, before there was a consensus about the design of `Pin`, this Early on, before there was a consensus about the design of `Pin`, this
@@ -206,18 +212,17 @@ We could forbid that, but **one of the major design goals for the async/await sy
to allow this**. These kinds of borrows were not possible using `Futures 1.0` so we can't let this to allow this**. These kinds of borrows were not possible using `Futures 1.0` so we can't let this
limitation just slip and call it a day yet. limitation just slip and call it a day yet.
Instead of discussing it in theory, let's look at some code. Instead of discussing it in theory, let's look at some code.
> We'll use the optimized version of the state machines which is used in Rust today. For a more > We'll use the optimized version of the state machines which is used in Rust today. For a more
> in deapth explanation see [Tyler Mandry's excellent article: How Rust optimizes async/await][optimizing-await] > in depth explanation see [Tyler Mandry's excellent article: How Rust optimizes async/await][optimizing-await]
```rust,noplaypen,ignore ```rust,noplaypen,ignore
let a = 4; let mut gen = move || {
let b = move || { let to_borrow = String::from("Hello");
let to_borrow = String::new("Hello");
let borrowed = &to_borrow; let borrowed = &to_borrow;
println!("{}", borrowed); println!("{}", borrowed);
yield a * 2; yield borrowed.len();
println!("{} world!", borrowed); println!("{} world!", borrowed);
}; };
``` ```
@@ -263,8 +268,10 @@ impl Generator for GeneratorA {
GeneratorA::Enter => { GeneratorA::Enter => {
let to_borrow = String::from("Hello"); let to_borrow = String::from("Hello");
let borrowed = &to_borrow; let borrowed = &to_borrow;
let res = borrowed.len();
*self = GeneratorA::Yield1 {to_borrow, borrowed}; *self = GeneratorA::Yield1 {to_borrow, borrowed};
GeneratorState::Yielded(borrowed.len()) GeneratorState::Yielded(res)
} }
GeneratorA::Yield1 {to_borrow, borrowed} => { GeneratorA::Yield1 {to_borrow, borrowed} => {
@@ -383,7 +390,7 @@ But now, let's prevent this problem using `Pin`. We'll discuss
reading the comments. reading the comments.
```rust,editable ```rust,editable
#![feature(optin_builtin_traits)] #![feature(optin_builtin_traits)] // needed to implement `!Unpin`
use std::pin::Pin; use std::pin::Pin;
pub fn main() { pub fn main() {
@@ -407,7 +414,7 @@ pub fn main() {
//let mut pinned2 = unsafe { Pin::new_unchecked(&mut gen2) }; //let mut pinned2 = unsafe { Pin::new_unchecked(&mut gen2) };
if let GeneratorState::Yielded(n) = pinned1.as_mut().resume() { if let GeneratorState::Yielded(n) = pinned1.as_mut().resume() {
println!("Got value {}", n); println!("Gen1 got value {}", n);
} }
if let GeneratorState::Yielded(n) = pinned2.as_mut().resume() { if let GeneratorState::Yielded(n) = pinned2.as_mut().resume() {

View File

@@ -95,6 +95,8 @@ articles I've already linked to in the book, here are some of my suggestions:
[Jon Gjengset's video on The Why, What and How of Pinning in Rust](https://youtu.be/DkMwYxfSYNQ) [Jon Gjengset's video on The Why, What and How of Pinning in Rust](https://youtu.be/DkMwYxfSYNQ)
[Withoutboats blog series about async/await](https://boats.gitlab.io/blog/post/2018-01-25-async-i-self-referential-structs/)
[condvar_std]: https://doc.rust-lang.org/stable/std/sync/struct.Condvar.html [condvar_std]: https://doc.rust-lang.org/stable/std/sync/struct.Condvar.html
[condvar_wiki]: https://en.wikipedia.org/wiki/Monitor_(synchronization)#Condition_variables [condvar_wiki]: https://en.wikipedia.org/wiki/Monitor_(synchronization)#Condition_variables
[arcwake]: https://rust-lang-nursery.github.io/futures-api-docs/0.3.0-alpha.13/futures/task/trait.ArcWake.html [arcwake]: https://rust-lang-nursery.github.io/futures-api-docs/0.3.0-alpha.13/futures/task/trait.ArcWake.html