merged with latest changes and made some additional corrections

This commit is contained in:
Carl Fredrik Samson
2020-04-10 20:39:35 +02:00
19 changed files with 488 additions and 487 deletions

View File

@@ -157,7 +157,7 @@
<li>See first hand why we need <code>Pin</code></li>
<li>Understand what makes Rusts async model very memory efficient</li>
</ul>
<p>The motivation for <code>Generators</code> can be found in <a href="https://github.com/rust-lang/rfcs/blob/master/text/2033-experimental-coroutines.md">RFC#2033</a>. It's very
<p>The motivation for <code>Generator</code>s can be found in <a href="https://github.com/rust-lang/rfcs/blob/master/text/2033-experimental-coroutines.md">RFC#2033</a>. It's very
well written and I can recommend reading through it (it talks as much about
async/await as it does about generators).</p>
</blockquote>
@@ -182,7 +182,7 @@ handle concurrency:</p>
so we won't repeat that here. We'll concentrate on the variants of stackless
coroutines which Rust uses today.</p>
<h3><a class="header" href="#combinators" id="combinators">Combinators</a></h3>
<p><code>Futures 0.1</code> used combinators. If you've worked with <code>Promises</code> in JavaScript,
<p><code>Futures 0.1</code> used combinators. If you've worked with Promises in JavaScript,
you already know combinators. In Rust they look like this:</p>
<pre><code class="language-rust noplaypen ignore">let future = Connection::connect(conn_str).and_then(|conn| {
conn.query(&quot;somerequest&quot;).map(|row|{
@@ -227,7 +227,7 @@ async/await as keywords (it can even be done using a macro).</li>
</code></pre>
<p>Async in Rust is implemented using Generators. So to understand how async really
works we need to understand generators first. Generators in Rust are implemented
as state machines. </p>
as state machines.</p>
<p>The memory footprint of a chain of computations is defined by <em>the largest footprint
that a single step requires</em>.</p>
<p>That means that adding steps to a chain of computations might not require any
@@ -330,7 +330,7 @@ impl Generator for GeneratorA {
</blockquote>
<p>Now that you know that the <code>yield</code> keyword in reality rewrites your code to become a state machine,
you'll also know the basics of how <code>await</code> works. It's very similar.</p>
<p>Now, there are some limitations in our naive state machine above. What happens when you have a
<p>Now, there are some limitations in our naive state machine above. What happens when you have a
<code>borrow</code> across a <code>yield</code> point?</p>
<p>We could forbid that, but <strong>one of the major design goals for the async/await syntax has been
to allow this</strong>. These kinds of borrows were not possible using <code>Futures 0.1</code> so we can't let this
@@ -363,10 +363,10 @@ Just keep this in the back of your head as we move forward.</p>
# #![allow(unused_variables)]
#fn main() {
# enum GeneratorState&lt;Y, R&gt; {
# Yielded(Y),
# Yielded(Y),
# Complete(R),
# }
#
#
# trait Generator {
# type Yield;
# type Return;
@@ -426,8 +426,8 @@ into itself.</p>
# #![allow(unused_variables)]
#fn main() {
enum GeneratorState&lt;Y, R&gt; {
Yielded(Y),
Complete(R),
Yielded(Y),
Complete(R),
}
trait Generator {
@@ -460,12 +460,12 @@ impl Generator for GeneratorA {
let borrowed = &amp;to_borrow;
let res = borrowed.len();
*self = GeneratorA::Yield1 {to_borrow, borrowed: std::ptr::null()};
// NB! And we set the pointer to reference the to_borrow string here
if let GeneratorA::Yield1 {to_borrow, borrowed} = self {
*borrowed = to_borrow;
}
GeneratorState::Yielded(res)
}
@@ -507,16 +507,16 @@ does what we'd expect. But there is still one huge problem with this:</p>
};
}
# enum GeneratorState&lt;Y, R&gt; {
# Yielded(Y),
# Complete(R),
# Yielded(Y),
# Complete(R),
# }
#
#
# trait Generator {
# type Yield;
# type Return;
# fn resume(&amp;mut self) -&gt; GeneratorState&lt;Self::Yield, Self::Return&gt;;
# }
#
#
# enum GeneratorA {
# Enter,
# Yield1 {
@@ -525,7 +525,7 @@ does what we'd expect. But there is still one huge problem with this:</p>
# },
# Exit,
# }
#
#
# impl GeneratorA {
# fn start() -&gt; Self {
# GeneratorA::Enter
@@ -541,15 +541,15 @@ does what we'd expect. But there is still one huge problem with this:</p>
# let borrowed = &amp;to_borrow;
# let res = borrowed.len();
# *self = GeneratorA::Yield1 {to_borrow, borrowed: std::ptr::null()};
#
#
# // We set the self-reference here
# if let GeneratorA::Yield1 {to_borrow, borrowed} = self {
# *borrowed = to_borrow;
# }
#
#
# GeneratorState::Yielded(res)
# }
#
#
# GeneratorA::Yield1 {borrowed, ..} =&gt; {
# let borrowed: &amp;String = unsafe {&amp;**borrowed};
# println!(&quot;{} world&quot;, borrowed);
@@ -585,16 +585,16 @@ pub fn main() {
};
}
# enum GeneratorState&lt;Y, R&gt; {
# Yielded(Y),
# Complete(R),
# Yielded(Y),
# Complete(R),
# }
#
#
# trait Generator {
# type Yield;
# type Return;
# fn resume(&amp;mut self) -&gt; GeneratorState&lt;Self::Yield, Self::Return&gt;;
# }
#
#
# enum GeneratorA {
# Enter,
# Yield1 {
@@ -603,7 +603,7 @@ pub fn main() {
# },
# Exit,
# }
#
#
# impl GeneratorA {
# fn start() -&gt; Self {
# GeneratorA::Enter
@@ -619,15 +619,15 @@ pub fn main() {
# let borrowed = &amp;to_borrow;
# let res = borrowed.len();
# *self = GeneratorA::Yield1 {to_borrow, borrowed: std::ptr::null()};
#
#
# // We set the self-reference here
# if let GeneratorA::Yield1 {to_borrow, borrowed} = self {
# *borrowed = to_borrow;
# }
#
#
# GeneratorState::Yielded(res)
# }
#
#
# GeneratorA::Yield1 {borrowed, ..} =&gt; {
# let borrowed: &amp;String = unsafe {&amp;**borrowed};
# println!(&quot;{} world&quot;, borrowed);
@@ -646,7 +646,7 @@ while using just safe Rust. This is a big problem!</p>
<blockquote>
<p>I've actually forced the code above to use the nightly version of the compiler.
If you run <a href="https://play.rust-lang.org/?version=stable&amp;mode=debug&amp;edition=2018&amp;gist=5cbe9897c0e23a502afd2740c7e78b98">the example above on the playground</a>,
you'll see that it runs without panicing on the current stable (1.42.0) but
you'll see that it runs without panicking on the current stable (1.42.0) but
panics on the current nightly (1.44.0). Scary!</p>
</blockquote>
<p>We'll explain exactly what happened here using a slightly simpler example in the next
@@ -707,7 +707,7 @@ pub fn main() {
yield borrowed.len();
println!(&quot;{} world!&quot;, borrowed);
};
let gen2 = static || {
let to_borrow = String::from(&quot;Hello&quot;);
let borrowed = &amp;to_borrow;
@@ -721,7 +721,7 @@ pub fn main() {
if let GeneratorState::Yielded(n) = pinned1.as_mut().resume(()) {
println!(&quot;Gen1 got value {}&quot;, n);
}
if let GeneratorState::Yielded(n) = pinned2.as_mut().resume(()) {
println!(&quot;Gen2 got value {}&quot;, n);
};