Files
docs-rust/ch17/ch17-05-traits-for-async.html
2026-06-22 21:27:36 +05:30

564 lines
37 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>A Closer Look at the Traits for Async</title>
</head>
<body>
<!-- Old headings. Do not remove or links may break. -->
<p><a id="digging-into-the-traits-for-async"></a></p>
<h2 id="a-closer-look-at-the-traits-for-async"><a class="header" href="#a-closer-look-at-the-traits-for-async">A Closer Look at the Traits for Async</a></h2>
<p>Throughout the chapter, weve used the <code>Future</code>, <code>Stream</code>, and <code>StreamExt</code>
traits in various ways. So far, though, weve avoided getting too far into the
details of how they work or how they fit together, which is fine most of the
time for your day-to-day Rust work. Sometimes, though, youll encounter
situations where youll need to understand a few more of these traits details,
along with the <code>Pin</code> type and the <code>Unpin</code> trait. In this section, well dig in
just enough to help in those scenarios, still leaving the <em>really</em> deep dive
for other documentation.</p>
<!-- Old headings. Do not remove or links may break. -->
<p><a id="future"></a></p>
<h3 id="the-future-trait"><a class="header" href="#the-future-trait">The <code>Future</code> Trait</a></h3>
<p>Lets start by taking a closer look at how the <code>Future</code> trait works. Heres how
Rust defines it:</p>
<pre class="playground"><code class="language-rust edition2024"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>use std::pin::Pin;
use std::task::{Context, Poll};
pub trait Future {
type Output;
fn poll(self: Pin&lt;&amp;mut Self&gt;, cx: &amp;mut Context&lt;'_&gt;) -&gt; Poll&lt;Self::Output&gt;;
}
<span class="boring">}</span></code></pre>
<p>That trait definition includes a bunch of new types and also some syntax we
havent seen before, so lets walk through the definition piece by piece.</p>
<p>First, <code>Future</code>s associated type <code>Output</code> says what the future resolves to.
This is analogous to the <code>Item</code> associated type for the <code>Iterator</code> trait.
Second, <code>Future</code> has the <code>poll</code> method, which takes a special <code>Pin</code> reference
for its <code>self</code> parameter and a mutable reference to a <code>Context</code> type, and
returns a <code>Poll&lt;Self::Output&gt;</code>. Well talk more about <code>Pin</code> and <code>Context</code> in a
moment. For now, lets focus on what the method returns, the <code>Poll</code> type:</p>
<pre class="playground"><code class="language-rust edition2024"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub enum Poll&lt;T&gt; {
Ready(T),
Pending,
}
<span class="boring">}</span></code></pre>
<p>This <code>Poll</code> type is similar to an <code>Option</code>. It has one variant that has a value,
<code>Ready(T)</code>, and one that does not, <code>Pending</code>. <code>Poll</code> means something quite
different from <code>Option</code>, though! The <code>Pending</code> variant indicates that the future
still has work to do, so the caller will need to check again later. The <code>Ready</code>
variant indicates that the <code>Future</code> has finished its work and the <code>T</code> value is
available.</p>
<section class="note" aria-role="note">
<p>Note: Its rare to need to call <code>poll</code> directly, but if you do need to, keep
in mind that with most futures, the caller should not call <code>poll</code> again after
the future has returned <code>Ready</code>. Many futures will panic if polled again after
becoming ready. Futures that are safe to poll again will say so explicitly in
their documentation. This is similar to how <code>Iterator::next</code> behaves.</p>
</section>
<p>When you see code that uses <code>await</code>, Rust compiles it under the hood to code
that calls <code>poll</code>. If you look back at Listing 17-4, where we printed out the
page title for a single URL once it resolved, Rust compiles it into something
kind of (although not exactly) like this:</p>
<pre><code class="language-rust ignore">match page_title(url).poll() {
Ready(page_title) =&gt; match page_title {
Some(title) =&gt; println!("The title for {url} was {title}"),
None =&gt; println!("{url} had no title"),
}
Pending =&gt; {
// But what goes here?
}
}</code></pre>
<p>What should we do when the future is still <code>Pending</code>? We need some way to try
again, and again, and again, until the future is finally ready. In other words,
we need a loop:</p>
<pre><code class="language-rust ignore">let mut page_title_fut = page_title(url);
loop {
match page_title_fut.poll() {
Ready(value) =&gt; match page_title {
Some(title) =&gt; println!("The title for {url} was {title}"),
None =&gt; println!("{url} had no title"),
}
Pending =&gt; {
// continue
}
}
}</code></pre>
<p>If Rust compiled it to exactly that code, though, every <code>await</code> would be
blocking—exactly the opposite of what we were going for! Instead, Rust ensures
that the loop can hand off control to something that can pause work on this
future to work on other futures and then check this one again later. As weve
seen, that something is an async runtime, and this scheduling and coordination
work is one of its main jobs.</p>
<p>In the <a href="ch17-02-concurrency-with-async.html#sending-data-between-two-tasks-using-message-passing">“Sending Data Between Two Tasks Using Message
Passing”</a><!-- ignore --> section, we described waiting on
<code>rx.recv</code>. The <code>recv</code> call returns a future, and awaiting the future polls it.
We noted that a runtime will pause the future until its ready with either
<code>Some(message)</code> or <code>None</code> when the channel closes. With our deeper
understanding of the <code>Future</code> trait, and specifically <code>Future::poll</code>, we can
see how that works. The runtime knows the future isnt ready when it returns
<code>Poll::Pending</code>. Conversely, the runtime knows the future <em>is</em> ready and
advances it when <code>poll</code> returns <code>Poll::Ready(Some(message))</code> or
<code>Poll::Ready(None)</code>.</p>
<p>The exact details of how a runtime does that are beyond the scope of this book,
but the key is to see the basic mechanics of futures: a runtime <em>polls</em> each
future it is responsible for, putting the future back to sleep when it is not
yet ready.</p>
<!-- Old headings. Do not remove or links may break. -->
<p><a id="pinning-and-the-pin-and-unpin-traits"></a>
<a id="the-pin-and-unpin-traits"></a></p>
<h3 id="the-pin-type-and-the-unpin-trait"><a class="header" href="#the-pin-type-and-the-unpin-trait">The <code>Pin</code> Type and the <code>Unpin</code> Trait</a></h3>
<p>Back in Listing 17-13, we used the <code>trpl::join!</code> macro to await three
futures. However, its common to have a collection such as a vector containing
some number futures that wont be known until runtime. Lets change Listing
17-13 to the code in Listing 17-23 that puts the three futures into a vector
and calls the <code>trpl::join_all</code> function instead, which wont compile yet.</p>
<figure class="listing" id="listing-17-23">
<span class="file-name">Filename: src/main.rs</span>
<pre><code class="language-rust ignore does_not_compile"><span class="boring">extern crate trpl; // required for mdbook test
</span><span class="boring">
</span><span class="boring">use std::time::Duration;
</span><span class="boring">
</span><span class="boring">fn main() {
</span><span class="boring"> trpl::block_on(async {
</span><span class="boring"> let (tx, mut rx) = trpl::channel();
</span><span class="boring">
</span><span class="boring"> let tx1 = tx.clone();
</span><span class="boring"> let tx1_fut = async move {
</span><span class="boring"> let vals = vec![
</span><span class="boring"> String::from("hi"),
</span><span class="boring"> String::from("from"),
</span><span class="boring"> String::from("the"),
</span><span class="boring"> String::from("future"),
</span><span class="boring"> ];
</span><span class="boring">
</span><span class="boring"> for val in vals {
</span><span class="boring"> tx1.send(val).unwrap();
</span><span class="boring"> trpl::sleep(Duration::from_secs(1)).await;
</span><span class="boring"> }
</span><span class="boring"> };
</span><span class="boring">
</span><span class="boring"> let rx_fut = async {
</span><span class="boring"> while let Some(value) = rx.recv().await {
</span><span class="boring"> println!("received '{value}'");
</span><span class="boring"> }
</span><span class="boring"> };
</span><span class="boring">
</span> let tx_fut = async move {
// --snip--
<span class="boring"> let vals = vec![
</span><span class="boring"> String::from("more"),
</span><span class="boring"> String::from("messages"),
</span><span class="boring"> String::from("for"),
</span><span class="boring"> String::from("you"),
</span><span class="boring"> ];
</span><span class="boring">
</span><span class="boring"> for val in vals {
</span><span class="boring"> tx.send(val).unwrap();
</span><span class="boring"> trpl::sleep(Duration::from_secs(1)).await;
</span><span class="boring"> }
</span> };
let futures: Vec&lt;Box&lt;dyn Future&lt;Output = ()&gt;&gt;&gt; =
vec![Box::new(tx1_fut), Box::new(rx_fut), Box::new(tx_fut)];
trpl::join_all(futures).await;
<span class="boring"> });
</span><span class="boring">}</span></code></pre>
<figcaption><a href="#listing-17-23">Listing 17-23</a>: Awaiting futures in a collection</figcaption>
</figure>
<p>We put each future within a <code>Box</code> to make them into <em>trait objects</em>, just as
we did in the “Returning Errors from <code>run</code>” section in Chapter 12. (Well cover
trait objects in detail in Chapter 18.) Using trait objects lets us treat each
of the anonymous futures produced by these types as the same type, because all
of them implement the <code>Future</code> trait.</p>
<p>This might be surprising. After all, none of the async blocks returns anything,
so each one produces a <code>Future&lt;Output = ()&gt;</code>. Remember that <code>Future</code> is a
trait, though, and that the compiler creates a unique enum for each async
block, even when they have identical output types. Just as you cant put two
different handwritten structs in a <code>Vec</code>, you cant mix compiler-generated
enums.</p>
<p>Then we pass the collection of futures to the <code>trpl::join_all</code> function and
await the result. However, this doesnt compile; heres the relevant part of
the error messages.</p>
<!-- manual-regeneration
cd listings/ch17-async-await/listing-17-23
cargo build
copy *only* the final `error` block from the errors
-->
<pre><code class="language-text">error[E0277]: `dyn Future&lt;Output = ()&gt;` cannot be unpinned
--&gt; src/main.rs:48:33
|
48 | trpl::join_all(futures).await;
| ^^^^^ the trait `Unpin` is not implemented for `dyn Future&lt;Output = ()&gt;`
|
= note: consider using the `pin!` macro
consider using `Box::pin` if you need to access the pinned value outside of the current scope
= note: required for `Box&lt;dyn Future&lt;Output = ()&gt;&gt;` to implement `Future`
note: required by a bound in `futures_util::future::join_all::JoinAll`
--&gt; file:///home/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-util-0.3.30/src/future/join_all.rs:29:8
|
27 | pub struct JoinAll&lt;F&gt;
| ------- required by a bound in this struct
28 | where
29 | F: Future,
| ^^^^^^ required by this bound in `JoinAll`
</code></pre>
<p>The note in this error message tells us that we should use the <code>pin!</code> macro to
<em>pin</em> the values, which means putting them inside the <code>Pin</code> type that
guarantees the values wont be moved in memory. The error message says pinning
is required because <code>dyn Future&lt;Output = ()&gt;</code> needs to implement the <code>Unpin</code>
trait and it currently does not.</p>
<p>The <code>trpl::join_all</code> function returns a struct called <code>JoinAll</code>. That struct is
generic over a type <code>F</code>, which is constrained to implement the <code>Future</code> trait.
Directly awaiting a future with <code>await</code> pins the future implicitly. Thats why
we dont need to use <code>pin!</code> everywhere we want to await futures.</p>
<p>However, were not directly awaiting a future here. Instead, we construct a new
future, JoinAll, by passing a collection of futures to the <code>join_all</code> function.
The signature for <code>join_all</code> requires that the types of the items in the
collection all implement the <code>Future</code> trait, and <code>Box&lt;T&gt;</code> implements <code>Future</code>
only if the <code>T</code> it wraps is a future that implements the <code>Unpin</code> trait.</p>
<p>Thats a lot to absorb! To really understand it, lets dive a little further
into how the <code>Future</code> trait actually works, in particular around pinning. Look
again at the definition of the <code>Future</code> trait:</p>
<pre class="playground"><code class="language-rust edition2024"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>use std::pin::Pin;
use std::task::{Context, Poll};
pub trait Future {
type Output;
// Required method
fn poll(self: Pin&lt;&amp;mut Self&gt;, cx: &amp;mut Context&lt;'_&gt;) -&gt; Poll&lt;Self::Output&gt;;
}
<span class="boring">}</span></code></pre>
<p>The <code>cx</code> parameter and its <code>Context</code> type are the key to how a runtime actually
knows when to check any given future while still being lazy. Again, the details
of how that works are beyond the scope of this chapter, and you generally only
need to think about this when writing a custom <code>Future</code> implementation. Well
focus instead on the type for <code>self</code>, as this is the first time weve seen a
method where <code>self</code> has a type annotation. A type annotation for <code>self</code> works
like type annotations for other function parameters but with two key
differences:</p>
<ul>
<li>It tells Rust what type <code>self</code> must be for the method to be called.</li>
<li>It cant be just any type. Its restricted to the type on which the method is
implemented, a reference or smart pointer to that type, or a <code>Pin</code> wrapping a
reference to that type.</li>
</ul>
<p>Well see more on this syntax in <a href="../ch18/ch18-00-oop.html">Chapter 18</a><!-- ignore -->. For now,
its enough to know that if we want to poll a future to check whether it is
<code>Pending</code> or <code>Ready(Output)</code>, we need a <code>Pin</code>-wrapped mutable reference to the
type.</p>
<p><code>Pin</code> is a wrapper for pointer-like types such as <code>&amp;</code>, <code>&amp;mut</code>, <code>Box</code>, and <code>Rc</code>.
(Technically, <code>Pin</code> works with types that implement the <code>Deref</code> or <code>DerefMut</code>
traits, but this is effectively equivalent to working only with references and
smart pointers.) <code>Pin</code> is not a pointer itself and doesnt have any behavior of
its own like <code>Rc</code> and <code>Arc</code> do with reference counting; its purely a tool the
compiler can use to enforce constraints on pointer usage.</p>
<p>Recalling that <code>await</code> is implemented in terms of calls to <code>poll</code> starts to
explain the error message we saw earlier, but that was in terms of <code>Unpin</code>, not
<code>Pin</code>. So how exactly does <code>Pin</code> relate to <code>Unpin</code>, and why does <code>Future</code> need
<code>self</code> to be in a <code>Pin</code> type to call <code>poll</code>?</p>
<p>Remember from earlier in this chapter that a series of await points in a future
get compiled into a state machine, and the compiler makes sure that state
machine follows all of Rusts normal rules around safety, including borrowing
and ownership. To make that work, Rust looks at what data is needed between one
await point and either the next await point or the end of the async block. It
then creates a corresponding variant in the compiled state machine. Each
variant gets the access it needs to the data that will be used in that section
of the source code, whether by taking ownership of that data or by getting a
mutable or immutable reference to it.</p>
<p>So far, so good: if we get anything wrong about the ownership or references in
a given async block, the borrow checker will tell us. When we want to move
around the future that corresponds to that block—like moving it into a <code>Vec</code> to
pass to <code>join_all</code>—things get trickier.</p>
<p>When we move a future—whether by pushing it into a data structure to use as an
iterator with <code>join_all</code> or by returning it from a function—that actually means
moving the state machine Rust creates for us. And unlike most other types in
Rust, the futures Rust creates for async blocks can end up with references to
themselves in the fields of any given variant, as shown in the simplified illustration in Figure 17-4.</p>
<figure>
<img alt="A single-column, three-row table representing a future, fut1, which has data values 0 and 1 in the first two rows and an arrow pointing from the third row back to the second row, representing an internal reference within the future." src="../img/trpl17-04.svg" class="center" />
<figcaption>Figure 17-4: A self-referential data type</figcaption>
</figure>
<p>By default, though, any object that has a reference to itself is unsafe to move,
because references always point to the actual memory address of whatever they
refer to (see Figure 17-5). If you move the data structure itself, those
internal references will be left pointing to the old location. However, that
memory location is now invalid. For one thing, its value will not be updated
when you make changes to the data structure. For another—more important—thing,
the computer is now free to reuse that memory for other purposes! You could end
up reading completely unrelated data later.</p>
<figure>
<img alt="Two tables, depicting two futures, fut1 and fut2, each of which has one column and three rows, representing the result of having moved a future out of fut1 into fut2. The first, fut1, is grayed out, with a question mark in each index, representing unknown memory. The second, fut2, has 0 and 1 in the first and second rows and an arrow pointing from its third row back to the second row of fut1, representing a pointer that is referencing the old location in memory of the future before it was moved." src="../img/trpl17-05.svg" class="center" />
<figcaption>Figure 17-5: The unsafe result of moving a self-referential data type</figcaption>
</figure>
<p>Theoretically, the Rust compiler could try to update every reference to an
object whenever it gets moved, but that could add a lot of performance overhead,
especially if a whole web of references needs updating. If we could instead make
sure the data structure in question <em>doesnt move in memory</em>, we wouldnt have
to update any references. This is exactly what Rusts borrow checker is for:
in safe code, it prevents you from moving any item with an active reference to
it.</p>
<p><code>Pin</code> builds on that to give us the exact guarantee we need. When we <em>pin</em> a
value by wrapping a pointer to that value in <code>Pin</code>, it can no longer move. Thus,
if you have <code>Pin&lt;Box&lt;SomeType&gt;&gt;</code>, you actually pin the <code>SomeType</code> value, <em>not</em>
the <code>Box</code> pointer. Figure 17-6 illustrates this process.</p>
<figure>
<img alt="Three boxes laid out side by side. The first is labeled “Pin”, the second “b1”, and the third “pinned”. Within “pinned” is a table labeled “fut”, with a single column; it represents a future with cells for each part of the data structure. Its first cell has the value “0”, its second cell has an arrow coming out of it and pointing to the fourth and final cell, which has the value “1” in it, and the third cell has dashed lines and an ellipsis to indicate there may be other parts to the data structure. All together, the “fut” table represents a future which is self-referential. An arrow leaves the box labeled “Pin”, goes through the box labeled “b1” and terminates inside the “pinned” box at the “fut” table." src="../img/trpl17-06.svg" class="center" />
<figcaption>Figure 17-6: Pinning a `Box` that points to a self-referential future type</figcaption>
</figure>
<p>In fact, the <code>Box</code> pointer can still move around freely. Remember: we care about
making sure the data ultimately being referenced stays in place. If a pointer
moves around, <em>but the data it points to</em> is in the same place, as in Figure
17-7, theres no potential problem. (As an independent exercise, look at the docs
for the types as well as the <code>std::pin</code> module and try to work out how youd do
this with a <code>Pin</code> wrapping a <code>Box</code>.) The key is that the self-referential type
itself cannot move, because it is still pinned.</p>
<figure>
<img alt="Four boxes laid out in three rough columns, identical to the previous diagram with a change to the second column. Now there are two boxes in the second column, labeled “b1” and “b2”, “b1” is grayed out, and the arrow from “Pin” goes through “b2” instead of “b1”, indicating that the pointer has moved from “b1” to “b2”, but the data in “pinned” has not moved." src="../img/trpl17-07.svg" class="center" />
<figcaption>Figure 17-7: Moving a `Box` which points to a self-referential future type</figcaption>
</figure>
<p>However, most types are perfectly safe to move around, even if they happen to be
behind a <code>Pin</code> pointer. We only need to think about pinning when items have
internal references. Primitive values such as numbers and Booleans are safe
because they obviously dont have any internal references.
Neither do most types you normally work with in Rust. You can move around
a <code>Vec</code>, for example, without worrying. Given what we have seen so far, if
you have a <code>Pin&lt;Vec&lt;String&gt;&gt;</code>, youd have to do everything via the safe but
restrictive APIs provided by <code>Pin</code>, even though a <code>Vec&lt;String&gt;</code> is always safe
to move if there are no other references to it. We need a way to tell the
compiler that its fine to move items around in cases like this—and thats
where <code>Unpin</code> comes into play.</p>
<p><code>Unpin</code> is a marker trait, similar to the <code>Send</code> and <code>Sync</code> traits we saw in
Chapter 16, and thus has no functionality of its own. Marker traits exist only
to tell the compiler its safe to use the type implementing a given trait in a
particular context. <code>Unpin</code> informs the compiler that a given type does <em>not</em>
need to uphold any guarantees about whether the value in question can be safely
moved.</p>
<!--
The inline `<code>` in the next block is to allow the inline `<em>` inside it,
matching what NoStarch does style-wise, and emphasizing within the text here
that it is something distinct from a normal type.
-->
<p>Just as with <code>Send</code> and <code>Sync</code>, the compiler implements <code>Unpin</code> automatically
for all types where it can prove it is safe. A special case, again similar to
<code>Send</code> and <code>Sync</code>, is where <code>Unpin</code> is <em>not</em> implemented for a type. The
notation for this is <code>impl !Unpin for <em>SomeType</em></code>, where
<code><em>SomeType</em></code> is the name of a type that <em>does</em> need to uphold
those guarantees to be safe whenever a pointer to that type is used in a <code>Pin</code>.</p>
<p>In other words, there are two things to keep in mind about the relationship
between <code>Pin</code> and <code>Unpin</code>. First, <code>Unpin</code> is the “normal” case, and <code>!Unpin</code> is
the special case. Second, whether a type implements <code>Unpin</code> or <code>!Unpin</code> <em>only</em>
matters when youre using a pinned pointer to that type like <code>Pin&lt;&amp;mut
<em>SomeType</em>&gt;</code>.</p>
<p>To make that concrete, think about a <code>String</code>: it has a length and the Unicode
characters that make it up. We can wrap a <code>String</code> in <code>Pin</code>, as seen in Figure
17-8. However, <code>String</code> automatically implements <code>Unpin</code>, as do most other types
in Rust.</p>
<figure>
<img alt="A box labeled “Pin” on the left with an arrow going from it to a box labeled “String” on the right. The “String” box contains the data 5usize, representing the length of the string, and the letters “h”, “e”, “l”, “l”, and “o” representing the characters of the string “hello” stored in this String instance. A dotted rectangle surrounds the “String” box and its label, but not the “Pin” box." src="../img/trpl17-08.svg" class="center" />
<figcaption>Figure 17-8: Pinning a `String`; the dotted line indicates that the `String` implements the `Unpin` trait and thus is not pinned</figcaption>
</figure>
<p>As a result, we can do things that would be illegal if <code>String</code> implemented
<code>!Unpin</code> instead, such as replacing one string with another at the exact same
location in memory as in Figure 17-9. This doesnt violate the <code>Pin</code> contract,
because <code>String</code> has no internal references that make it unsafe to move around.
That is precisely why it implements <code>Unpin</code> rather than <code>!Unpin</code>.</p>
<figure>
<img alt="The same “hello” string data from the previous example, now labeled “s1” and grayed out. The “Pin” box from the previous example now points to a different String instance, one that is labeled “s2”, is valid, has a length of 7usize, and contains the characters of the string “goodbye”. s2 is surrounded by a dotted rectangle because it, too, implements the Unpin trait." src="../img/trpl17-09.svg" class="center" />
<figcaption>Figure 17-9: Replacing the `String` with an entirely different `String` in memory</figcaption>
</figure>
<p>Now we know enough to understand the errors reported for that <code>join_all</code> call
from back in Listing 17-23. We originally tried to move the futures produced by
async blocks into a <code>Vec&lt;Box&lt;dyn Future&lt;Output = ()&gt;&gt;&gt;</code>, but as weve seen,
those futures may have internal references, so they dont automatically
implement <code>Unpin</code>. Once we pin them, we can pass the resulting <code>Pin</code> type into
the <code>Vec</code>, confident that the underlying data in the futures will <em>not</em> be
moved. Listing 17-24 shows how to fix the code by calling the <code>pin!</code> macro
where each of the three futures are defined and adjusting the trait object type.</p>
<figure class="listing" id="listing-17-24">
<pre class="playground"><code class="language-rust edition2024"><span class="boring">extern crate trpl; // required for mdbook test
</span><span class="boring">
</span>use std::pin::{Pin, pin};
// --snip--
<span class="boring">use std::time::Duration;
</span><span class="boring">
</span><span class="boring">fn main() {
</span><span class="boring"> trpl::block_on(async {
</span><span class="boring"> let (tx, mut rx) = trpl::channel();
</span><span class="boring">
</span><span class="boring"> let tx1 = tx.clone();
</span> let tx1_fut = pin!(async move {
// --snip--
<span class="boring"> let vals = vec![
</span><span class="boring"> String::from("hi"),
</span><span class="boring"> String::from("from"),
</span><span class="boring"> String::from("the"),
</span><span class="boring"> String::from("future"),
</span><span class="boring"> ];
</span><span class="boring">
</span><span class="boring"> for val in vals {
</span><span class="boring"> tx1.send(val).unwrap();
</span><span class="boring"> trpl::sleep(Duration::from_secs(1)).await;
</span><span class="boring"> }
</span> });
let rx_fut = pin!(async {
// --snip--
<span class="boring"> while let Some(value) = rx.recv().await {
</span><span class="boring"> println!("received '{value}'");
</span><span class="boring"> }
</span> });
let tx_fut = pin!(async move {
// --snip--
<span class="boring"> let vals = vec![
</span><span class="boring"> String::from("more"),
</span><span class="boring"> String::from("messages"),
</span><span class="boring"> String::from("for"),
</span><span class="boring"> String::from("you"),
</span><span class="boring"> ];
</span><span class="boring">
</span><span class="boring"> for val in vals {
</span><span class="boring"> tx.send(val).unwrap();
</span><span class="boring"> trpl::sleep(Duration::from_secs(1)).await;
</span><span class="boring"> }
</span> });
let futures: Vec&lt;Pin&lt;&amp;mut dyn Future&lt;Output = ()&gt;&gt;&gt; =
vec![tx1_fut, rx_fut, tx_fut];
<span class="boring">
</span><span class="boring"> trpl::join_all(futures).await;
</span><span class="boring"> });
</span><span class="boring">}</span></code></pre>
<figcaption><a href="#listing-17-24">Listing 17-24</a>: Pinning the futures to enable moving them into the vector</figcaption>
</figure>
<p>This example now compiles and runs, and we could add or remove futures from the
vector at runtime and join them all.</p>
<p><code>Pin</code> and <code>Unpin</code> are mostly important for building lower-level libraries, or
when youre building a runtime itself, rather than for day-to-day Rust code.
When you see these traits in error messages, though, now youll have a better
idea of how to fix your code!</p>
<section class="note" aria-role="note">
<p>Note: This combination of <code>Pin</code> and <code>Unpin</code> makes it possible to safely
implement a whole class of complex types in Rust that would otherwise prove
challenging because theyre self-referential. Types that require <code>Pin</code> show up
most commonly in async Rust today, but every once in a while, you might see
them in other contexts, too.</p>
<p>The specifics of how <code>Pin</code> and <code>Unpin</code> work, and the rules theyre required
to uphold, are covered extensively in the API documentation for <code>std::pin</code>, so
if youre interested in learning more, thats a great place to start.</p>
<p>If you want to understand how things work under the hood in even more detail,
see Chapters <a href="https://rust-lang.github.io/async-book/02_execution/01_chapter.html">2</a><!-- ignore --> and
<a href="https://rust-lang.github.io/async-book/04_pinning/01_chapter.html">4</a><!-- ignore --> of
<a href="https://rust-lang.github.io/async-book/"><em>Asynchronous Programming in Rust</em></a>.</p>
</section>
<h3 id="the-stream-trait"><a class="header" href="#the-stream-trait">The <code>Stream</code> Trait</a></h3>
<p>Now that you have a deeper grasp on the <code>Future</code>, <code>Pin</code>, and <code>Unpin</code> traits, we
can turn our attention to the <code>Stream</code> trait. As you learned earlier in the
chapter, streams are similar to asynchronous iterators. Unlike <code>Iterator</code> and
<code>Future</code>, however, <code>Stream</code> has no definition in the standard library as of
this writing, but there <em>is</em> a very common definition from the <code>futures</code> crate
used throughout the ecosystem.</p>
<p>Lets review the definitions of the <code>Iterator</code> and <code>Future</code> traits before
looking at how a <code>Stream</code> trait might merge them together. From <code>Iterator</code>, we
have the idea of a sequence: its <code>next</code> method provides an
<code>Option&lt;Self::Item&gt;</code>. From <code>Future</code>, we have the idea of readiness over time:
its <code>poll</code> method provides a <code>Poll&lt;Self::Output&gt;</code>. To represent a sequence of
items that become ready over time, we define a <code>Stream</code> trait that puts those
features together:</p>
<pre class="playground"><code class="language-rust edition2024"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>use std::pin::Pin;
use std::task::{Context, Poll};
trait Stream {
type Item;
fn poll_next(
self: Pin&lt;&amp;mut Self&gt;,
cx: &amp;mut Context&lt;'_&gt;
) -&gt; Poll&lt;Option&lt;Self::Item&gt;&gt;;
}
<span class="boring">}</span></code></pre>
<p>The <code>Stream</code> trait defines an associated type called <code>Item</code> for the type of the
items produced by the stream. This is similar to <code>Iterator</code>, where there may be
zero to many items, and unlike <code>Future</code>, where there is always a single
<code>Output</code>, even if its the unit type <code>()</code>.</p>
<p><code>Stream</code> also defines a method to get those items. We call it <code>poll_next</code>, to
make it clear that it polls in the same way <code>Future::poll</code> does and produces a
sequence of items in the same way <code>Iterator::next</code> does. Its return type
combines <code>Poll</code> with <code>Option</code>. The outer type is <code>Poll</code>, because it has to be
checked for readiness, just as a future does. The inner type is <code>Option</code>,
because it needs to signal whether there are more messages, just as an iterator
does.</p>
<p>Something very similar to this definition will likely end up as part of Rusts
standard library. In the meantime, its part of the toolkit of most runtimes,
so you can rely on it, and everything we cover next should generally apply!</p>
<p>In the examples we saw in the <a href="ch17-04-streams.html">“Streams: Futures in Sequence”</a><!--
ignore --> section, though, we didnt use <code>poll_next</code> <em>or</em> <code>Stream</code>, but
instead used <code>next</code> and <code>StreamExt</code>. We <em>could</em> work directly in terms of the
<code>poll_next</code> API by hand-writing our own <code>Stream</code> state machines, of course,
just as we <em>could</em> work with futures directly via their <code>poll</code> method. Using
<code>await</code> is much nicer, though, and the <code>StreamExt</code> trait supplies the <code>next</code>
method so we can do just that:</p>
<pre class="playground"><code class="language-rust edition2024"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span><span class="boring">use std::pin::Pin;
</span><span class="boring">use std::task::{Context, Poll};
</span><span class="boring">
</span><span class="boring">trait Stream {
</span><span class="boring"> type Item;
</span><span class="boring"> fn poll_next(
</span><span class="boring"> self: Pin&lt;&amp;mut Self&gt;,
</span><span class="boring"> cx: &amp;mut Context&lt;'_&gt;,
</span><span class="boring"> ) -&gt; Poll&lt;Option&lt;Self::Item&gt;&gt;;
</span><span class="boring">}
</span><span class="boring">
</span>trait StreamExt: Stream {
async fn next(&amp;mut self) -&gt; Option&lt;Self::Item&gt;
where
Self: Unpin;
// other methods...
}
<span class="boring">}</span></code></pre>
<!--
TODO: update this if/when tokio/etc. update their MSRV and switch to using async functions
in traits, since the lack thereof is the reason they do not yet have this.
-->
<section class="note" aria-role="note">
<p>Note: The actual definition we used earlier in the chapter looks slightly
different than this, because it supports versions of Rust that did not yet
support using async functions in traits. As a result, it looks like this:</p>
<pre><code class="language-rust ignore">fn next(&amp;mut self) -&gt; Next&lt;'_, Self&gt; where Self: Unpin;</code></pre>
<p>That <code>Next</code> type is a <code>struct</code> that implements <code>Future</code> and allows us to name
the lifetime of the reference to <code>self</code> with <code>Next&lt;'_, Self&gt;</code>, so that <code>await</code>
can work with this method.</p>
</section>
<p>The <code>StreamExt</code> trait is also the home of all the interesting methods available
to use with streams. <code>StreamExt</code> is automatically implemented for every type
that implements <code>Stream</code>, but these traits are defined separately to enable the
community to iterate on convenience APIs without affecting the foundational
trait.</p>
<p>In the version of <code>StreamExt</code> used in the <code>trpl</code> crate, the trait not only
defines the <code>next</code> method but also supplies a default implementation of <code>next</code>
that correctly handles the details of calling <code>Stream::poll_next</code>. This means
that even when you need to write your own streaming data type, you <em>only</em> have
to implement <code>Stream</code>, and then anyone who uses your data type can use
<code>StreamExt</code> and its methods with it automatically.</p>
<p>Thats all were going to cover for the lower-level details on these traits. To
wrap up, lets consider how futures (including streams), tasks, and threads all
fit together!</p>
</body>
</html>