From 53529fa7697fdc6c300ec27cae95ba79c94087f9 Mon Sep 17 00:00:00 2001 From: Carl Fredrik Samson Date: Wed, 5 Feb 2020 22:41:14 +0100 Subject: [PATCH] fixed some of the generator examples that were a bit unprecise --- src/3_generators_pin.md | 41 ++++++++++++++++++++++++----------------- src/conclusion.md | 2 ++ 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/src/3_generators_pin.md b/src/3_generators_pin.md index 92831c6..27e8a65 100644 --- a/src/3_generators_pin.md +++ b/src/3_generators_pin.md @@ -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 3. No need to handle dynamic stack allocation 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: @@ -101,24 +101,30 @@ require any increased memory at all. ## How generators work 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 -(I'm going to use the terminology that's currently in Rust): +keyword in a closure, converts it to a generator. A closure could look like this +before we had a concept of `Pin`: + ```rust,noplaypen,ignore -let a = 4; -let b = move || { +#![feature(generators, generator_trait)] +use std::ops::{Generator, GeneratorState}; + +fn main() { + let a: i32 = 4; + let mut gen = move || { println!("Hello"); yield a * 2; println!("world!"); }; -if let GeneratorState::Yielded(n) = gen.resume() { + if let GeneratorState::Yielded(n) = gen.resume() { 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 @@ -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 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 -> 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 -let a = 4; -let b = move || { - let to_borrow = String::new("Hello"); +let mut gen = move || { + let to_borrow = String::from("Hello"); let borrowed = &to_borrow; println!("{}", borrowed); - yield a * 2; + yield borrowed.len(); println!("{} world!", borrowed); }; ``` @@ -263,8 +268,10 @@ impl Generator for GeneratorA { GeneratorA::Enter => { let to_borrow = String::from("Hello"); let borrowed = &to_borrow; + let res = borrowed.len(); + *self = GeneratorA::Yield1 {to_borrow, borrowed}; - GeneratorState::Yielded(borrowed.len()) + GeneratorState::Yielded(res) } GeneratorA::Yield1 {to_borrow, borrowed} => { @@ -383,7 +390,7 @@ But now, let's prevent this problem using `Pin`. We'll discuss reading the comments. ```rust,editable -#![feature(optin_builtin_traits)] +#![feature(optin_builtin_traits)] // needed to implement `!Unpin` use std::pin::Pin; pub fn main() { @@ -407,7 +414,7 @@ pub fn main() { //let mut pinned2 = unsafe { Pin::new_unchecked(&mut gen2) }; 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() { diff --git a/src/conclusion.md b/src/conclusion.md index 8ba3b67..ebd557f 100644 --- a/src/conclusion.md +++ b/src/conclusion.md @@ -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) +[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_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 \ No newline at end of file