438 lines
28 KiB
HTML
438 lines
28 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<title>Futures and the Async Syntax</title>
|
||
</head>
|
||
<body>
|
||
<h2 id="futures-and-the-async-syntax"><a class="header" href="#futures-and-the-async-syntax">Futures and the Async Syntax</a></h2>
|
||
<p>The key elements of asynchronous programming in Rust are <em>futures</em> and Rust’s
|
||
<code>async</code> and <code>await</code> keywords.</p>
|
||
<p>A <em>future</em> is a value that may not be ready now but will become ready at some
|
||
point in the future. (This same concept shows up in many languages, sometimes
|
||
under other names such as <em>task</em> or <em>promise</em>.) Rust provides a <code>Future</code> trait
|
||
as a building block so that different async operations can be implemented with
|
||
different data structures but with a common interface. In Rust, futures are
|
||
types that implement the <code>Future</code> trait. Each future holds its own information
|
||
about the progress that has been made and what “ready” means.</p>
|
||
<p>You can apply the <code>async</code> keyword to blocks and functions to specify that they
|
||
can be interrupted and resumed. Within an async block or async function, you
|
||
can use the <code>await</code> keyword to <em>await a future</em> (that is, wait for it to become
|
||
ready). Any point where you await a future within an async block or function is
|
||
a potential spot for that block or function to pause and resume. The process of
|
||
checking with a future to see if its value is available yet is called <em>polling</em>.</p>
|
||
<p>Some other languages, such as C# and JavaScript, also use <code>async</code> and <code>await</code>
|
||
keywords for async programming. If you’re familiar with those languages, you
|
||
may notice some significant differences in how Rust handles the syntax. That’s
|
||
for good reason, as we’ll see!</p>
|
||
<p>When writing async Rust, we use the <code>async</code> and <code>await</code> keywords most of the
|
||
time. Rust compiles them into equivalent code using the <code>Future</code> trait, much as
|
||
it compiles <code>for</code> loops into equivalent code using the <code>Iterator</code> trait.
|
||
Because Rust provides the <code>Future</code> trait, though, you can also implement it for
|
||
your own data types when you need to. Many of the functions we’ll see
|
||
throughout this chapter return types with their own implementations of
|
||
<code>Future</code>. We’ll return to the definition of the trait at the end of the chapter
|
||
and dig into more of how it works, but this is enough detail to keep us moving
|
||
forward.</p>
|
||
<p>This may all feel a bit abstract, so let’s write our first async program: a
|
||
little web scraper. We’ll pass in two URLs from the command line, fetch both of
|
||
them concurrently, and return the result of whichever one finishes first. This
|
||
example will have a fair bit of new syntax, but don’t worry—we’ll explain
|
||
everything you need to know as we go.</p>
|
||
<h2 id="our-first-async-program"><a class="header" href="#our-first-async-program">Our First Async Program</a></h2>
|
||
<p>To keep the focus of this chapter on learning async rather than juggling parts
|
||
of the ecosystem, we’ve created the <code>trpl</code> crate (<code>trpl</code> is short for “The Rust
|
||
Programming Language”). It re-exports all the types, traits, and functions
|
||
you’ll need, primarily from the <a href="https://crates.io/crates/futures"><code>futures</code></a><!-- ignore --> and
|
||
<a href="https://tokio.rs"><code>tokio</code></a><!-- ignore --> crates. The <code>futures</code> crate is an official home
|
||
for Rust experimentation for async code, and it’s actually where the <code>Future</code>
|
||
trait was originally designed. Tokio is the most widely used async runtime in
|
||
Rust today, especially for web applications. There are other great runtimes out
|
||
there, and they may be more suitable for your purposes. We use the <code>tokio</code>
|
||
crate under the hood for <code>trpl</code> because it’s well tested and widely used.</p>
|
||
<p>In some cases, <code>trpl</code> also renames or wraps the original APIs to keep you
|
||
focused on the details relevant to this chapter. If you want to understand what
|
||
the crate does, we encourage you to check out <a href="https://github.com/rust-lang/book/tree/main/packages/trpl">its source code</a>.
|
||
You’ll be able to see what crate each re-export comes from, and we’ve left
|
||
extensive comments explaining what the crate does.</p>
|
||
<p>Create a new binary project named <code>hello-async</code> and add the <code>trpl</code> crate as a
|
||
dependency:</p>
|
||
<pre><code class="language-console">$ cargo new hello-async
|
||
$ cd hello-async
|
||
$ cargo add trpl
|
||
</code></pre>
|
||
<p>Now we can use the various pieces provided by <code>trpl</code> to write our first async
|
||
program. We’ll build a little command line tool that fetches two web pages,
|
||
pulls the <code><title></code> element from each, and prints out the title of whichever
|
||
page finishes that whole process first.</p>
|
||
<h3 id="defining-the-page_title-function"><a class="header" href="#defining-the-page_title-function">Defining the page_title Function</a></h3>
|
||
<p>Let’s start by writing a function that takes one page URL as a parameter, makes
|
||
a request to it, and returns the text of the <code><title></code> element (see Listing
|
||
17-1).</p>
|
||
<figure class="listing" id="listing-17-1">
|
||
<span class="file-name">Filename: src/main.rs</span>
|
||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">extern crate trpl; // required for mdbook test
|
||
</span><span class="boring">
|
||
</span><span class="boring">fn main() {
|
||
</span><span class="boring"> // TODO: we'll add this next!
|
||
</span><span class="boring">}
|
||
</span><span class="boring">
|
||
</span>use trpl::Html;
|
||
|
||
async fn page_title(url: &str) -> Option<String> {
|
||
let response = trpl::get(url).await;
|
||
let response_text = response.text().await;
|
||
Html::parse(&response_text)
|
||
.select_first("title")
|
||
.map(|title| title.inner_html())
|
||
}</code></pre>
|
||
<figcaption><a href="#listing-17-1">Listing 17-1</a>: Defining an async function to get the title element from an HTML page</figcaption>
|
||
</figure>
|
||
<p>First, we define a function named <code>page_title</code> and mark it with the <code>async</code>
|
||
keyword. Then we use the <code>trpl::get</code> function to fetch whatever URL is passed
|
||
in and add the <code>await</code> keyword to await the response. To get the text of the
|
||
<code>response</code>, we call its <code>text</code> method and once again await it with the <code>await</code>
|
||
keyword. Both of these steps are asynchronous. For the <code>get</code> function, we have
|
||
to wait for the server to send back the first part of its response, which will
|
||
include HTTP headers, cookies, and so on and can be delivered separately from
|
||
the response body. Especially if the body is very large, it can take some time
|
||
for it all to arrive. Because we have to wait for the <em>entirety</em> of the
|
||
response to arrive, the <code>text</code> method is also async.</p>
|
||
<p>We have to explicitly await both of these futures, because futures in Rust are
|
||
<em>lazy</em>: they don’t do anything until you ask them to with the <code>await</code> keyword.
|
||
(In fact, Rust will show a compiler warning if you don’t use a future.) This
|
||
might remind you of the discussion of iterators in the <a href="../ch13/ch13-02-iterators.html">“Processing a Series of
|
||
Items with Iterators”</a><!-- ignore --> section in Chapter 13.
|
||
Iterators do nothing unless you call their <code>next</code> method—whether directly or by
|
||
using <code>for</code> loops or methods such as <code>map</code> that use <code>next</code> under the hood.
|
||
Likewise, futures do nothing unless you explicitly ask them to. This laziness
|
||
allows Rust to avoid running async code until it’s actually needed.</p>
|
||
<section class="note" aria-role="note">
|
||
<p>Note: This is different from the behavior we saw when using <code>thread::spawn</code>
|
||
in the <a href="../ch16/ch16-01-threads.html#creating-a-new-thread-with-spawn">“Creating a New Thread with spawn”</a><!-- ignore -->
|
||
section in Chapter 16, where the closure we passed to another thread started
|
||
running immediately. It’s also different from how many other languages
|
||
approach async. But it’s important for Rust to be able to provide its
|
||
performance guarantees, just as it is with iterators.</p>
|
||
</section>
|
||
<p>Once we have <code>response_text</code>, we can parse it into an instance of the <code>Html</code>
|
||
type using <code>Html::parse</code>. Instead of a raw string, we now have a data type we
|
||
can use to work with the HTML as a richer data structure. In particular, we can
|
||
use the <code>select_first</code> method to find the first instance of a given CSS
|
||
selector. By passing the string <code>"title"</code>, we’ll get the first <code><title></code>
|
||
element in the document, if there is one. Because there may not be any matching
|
||
element, <code>select_first</code> returns an <code>Option<ElementRef></code>. Finally, we use the
|
||
<code>Option::map</code> method, which lets us work with the item in the <code>Option</code> if it’s
|
||
present, and do nothing if it isn’t. (We could also use a <code>match</code> expression
|
||
here, but <code>map</code> is more idiomatic.) In the body of the function we supply to
|
||
<code>map</code>, we call <code>inner_html</code> on the <code>title</code> to get its content, which is a
|
||
<code>String</code>. When all is said and done, we have an <code>Option<String></code>.</p>
|
||
<p>Notice that Rust’s <code>await</code> keyword goes <em>after</em> the expression you’re awaiting,
|
||
not before it. That is, it’s a <em>postfix</em> keyword. This may differ from what
|
||
you’re used to if you’ve used <code>async</code> in other languages, but in Rust it makes
|
||
chains of methods much nicer to work with. As a result, we could change the
|
||
body of <code>page_title</code> to chain the <code>trpl::get</code> and <code>text</code> function calls
|
||
together with <code>await</code> between them, as shown in Listing 17-2.</p>
|
||
<figure class="listing" id="listing-17-2">
|
||
<span class="file-name">Filename: src/main.rs</span>
|
||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">extern crate trpl; // required for mdbook test
|
||
</span><span class="boring">
|
||
</span><span class="boring">use trpl::Html;
|
||
</span><span class="boring">
|
||
</span><span class="boring">fn main() {
|
||
</span><span class="boring"> // TODO: we'll add this next!
|
||
</span><span class="boring">}
|
||
</span><span class="boring">
|
||
</span><span class="boring">async fn page_title(url: &str) -> Option<String> {
|
||
</span> let response_text = trpl::get(url).await.text().await;
|
||
<span class="boring"> Html::parse(&response_text)
|
||
</span><span class="boring"> .select_first("title")
|
||
</span><span class="boring"> .map(|title| title.inner_html())
|
||
</span><span class="boring">}</span></code></pre>
|
||
<figcaption><a href="#listing-17-2">Listing 17-2</a>: Chaining with the <code>await</code> keyword</figcaption>
|
||
</figure>
|
||
<p>With that, we have successfully written our first async function! Before we add
|
||
some code in <code>main</code> to call it, let’s talk a little more about what we’ve
|
||
written and what it means.</p>
|
||
<p>When Rust sees a <em>block</em> marked with the <code>async</code> keyword, it compiles it into a
|
||
unique, anonymous data type that implements the <code>Future</code> trait. When Rust sees
|
||
a <em>function</em> marked with <code>async</code>, it compiles it into a non-async function
|
||
whose body is an async block. An async function’s return type is the type of
|
||
the anonymous data type the compiler creates for that async block.</p>
|
||
<p>Thus, writing <code>async fn</code> is equivalent to writing a function that returns a
|
||
<em>future</em> of the return type. To the compiler, a function definition such as the
|
||
<code>async fn page_title</code> in Listing 17-1 is roughly equivalent to a non-async
|
||
function defined like this:</p>
|
||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">#![allow(unused)]
|
||
</span><span class="boring">fn main() {
|
||
</span><span class="boring">extern crate trpl; // required for mdbook test
|
||
</span>use std::future::Future;
|
||
use trpl::Html;
|
||
|
||
fn page_title(url: &str) -> impl Future<Output = Option<String>> {
|
||
async move {
|
||
let text = trpl::get(url).await.text().await;
|
||
Html::parse(&text)
|
||
.select_first("title")
|
||
.map(|title| title.inner_html())
|
||
}
|
||
}
|
||
<span class="boring">}</span></code></pre>
|
||
<p>Let’s walk through each part of the transformed version:</p>
|
||
<ul>
|
||
<li>It uses the <code>impl Trait</code> syntax we discussed back in Chapter 10 in the
|
||
<a href="../ch10/ch10-02-traits.html#traits-as-parameters">“Traits as Parameters”</a><!-- ignore --> section.</li>
|
||
<li>The returned value implements the <code>Future</code> trait with an associated type of
|
||
<code>Output</code>. Notice that the <code>Output</code> type is <code>Option<String></code>, which is the
|
||
same as the original return type from the <code>async fn</code> version of <code>page_title</code>.</li>
|
||
<li>All of the code called in the body of the original function is wrapped in
|
||
an <code>async move</code> block. Remember that blocks are expressions. This whole block
|
||
is the expression returned from the function.</li>
|
||
<li>This async block produces a value with the type <code>Option<String></code>, as just
|
||
described. That value matches the <code>Output</code> type in the return type. This is
|
||
just like other blocks you have seen.</li>
|
||
<li>The new function body is an <code>async move</code> block because of how it uses the
|
||
<code>url</code> parameter. (We’ll talk much more about <code>async</code> versus <code>async move</code>
|
||
later in the chapter.)</li>
|
||
</ul>
|
||
<p>Now we can call <code>page_title</code> in <code>main</code>.</p>
|
||
<!-- Old headings. Do not remove or links may break. -->
|
||
<p><a id="determining-a-single-pages-title"></a></p>
|
||
<h3 id="executing-an-async-function-with-a-runtime"><a class="header" href="#executing-an-async-function-with-a-runtime">Executing an Async Function with a Runtime</a></h3>
|
||
<p>To start, we’ll get the title for a single page, shown in Listing 17-3.
|
||
Unfortunately, this code doesn’t compile yet.</p>
|
||
<figure class="listing" id="listing-17-3">
|
||
<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 trpl::Html;
|
||
</span><span class="boring">
|
||
</span>async fn main() {
|
||
let args: Vec<String> = std::env::args().collect();
|
||
let url = &args[1];
|
||
match page_title(url).await {
|
||
Some(title) => println!("The title for {url} was {title}"),
|
||
None => println!("{url} had no title"),
|
||
}
|
||
}
|
||
<span class="boring">
|
||
</span><span class="boring">async fn page_title(url: &str) -> Option<String> {
|
||
</span><span class="boring"> let response_text = trpl::get(url).await.text().await;
|
||
</span><span class="boring"> Html::parse(&response_text)
|
||
</span><span class="boring"> .select_first("title")
|
||
</span><span class="boring"> .map(|title| title.inner_html())
|
||
</span><span class="boring">}</span></code></pre>
|
||
<figcaption><a href="#listing-17-3">Listing 17-3</a>: Calling the <code>page_title</code> function from <code>main</code> with a user-supplied argument</figcaption>
|
||
</figure>
|
||
<p>We follow the same pattern we used to get command line arguments in the
|
||
<a href="../ch12/ch12-01-accepting-command-line-arguments.html">“Accepting Command Line Arguments”</a><!-- ignore --> section in
|
||
Chapter 12. Then we pass the URL argument to <code>page_title</code> and await the result.
|
||
Because the value produced by the future is an <code>Option<String></code>, we use a
|
||
<code>match</code> expression to print different messages to account for whether the page
|
||
had a <code><title></code>.</p>
|
||
<p>The only place we can use the <code>await</code> keyword is in async functions or blocks,
|
||
and Rust won’t let us mark the special <code>main</code> function as <code>async</code>.</p>
|
||
<!-- manual-regeneration
|
||
cd listings/ch17-async-await/listing-17-03
|
||
cargo build
|
||
copy just the compiler error
|
||
-->
|
||
<pre><code class="language-text">error[E0752]: `main` function is not allowed to be `async`
|
||
--> src/main.rs:6:1
|
||
|
|
||
6 | async fn main() {
|
||
| ^^^^^^^^^^^^^^^ `main` function is not allowed to be `async`
|
||
</code></pre>
|
||
<p>The reason <code>main</code> can’t be marked <code>async</code> is that async code needs a <em>runtime</em>:
|
||
a Rust crate that manages the details of executing asynchronous code. A
|
||
program’s <code>main</code> function can <em>initialize</em> a runtime, but it’s not a runtime
|
||
<em>itself</em>. (We’ll see more about why this is the case in a bit.) Every Rust
|
||
program that executes async code has at least one place where it sets up a
|
||
runtime that executes the futures.</p>
|
||
<p>Most languages that support async bundle a runtime, but Rust does not. Instead,
|
||
there are many different async runtimes available, each of which makes different
|
||
tradeoffs suitable to the use case it targets. For example, a high-throughput
|
||
web server with many CPU cores and a large amount of RAM has very different
|
||
needs than a microcontroller with a single core, a small amount of RAM, and no
|
||
heap allocation ability. The crates that provide those runtimes also often
|
||
supply async versions of common functionality such as file or network I/O.</p>
|
||
<p>Here, and throughout the rest of this chapter, we’ll use the <code>block_on</code>
|
||
function from the <code>trpl</code> crate, which takes a future as an argument and blocks
|
||
the current thread until this future runs to completion. Behind the scenes,
|
||
calling <code>block_on</code> sets up a runtime using the <code>tokio</code> crate that’s used to run
|
||
the future passed in (the <code>trpl</code> crate’s <code>block_on</code> behavior is similar to
|
||
other runtime crates’ <code>block_on</code> functions). Once the future completes,
|
||
<code>block_on</code> returns whatever value the future produced.</p>
|
||
<p>We could pass the future returned by <code>page_title</code> directly to <code>block_on</code> and,
|
||
once it completed, we could match on the resulting <code>Option<String></code> as we tried
|
||
to do in Listing 17-3. However, for most of the examples in the chapter (and
|
||
most async code in the real world), we’ll be doing more than just one async
|
||
function call, so instead we’ll pass an <code>async</code> block and explicitly await the
|
||
result of the <code>page_title</code> call, as in Listing 17-4.</p>
|
||
<figure class="listing" id="listing-17-4">
|
||
<span class="file-name">Filename: src/main.rs</span>
|
||
<!-- should_panic,noplayground because mdbook test does not pass args -->
|
||
<pre><code class="language-rust should_panic noplayground"><span class="boring">extern crate trpl; // required for mdbook test
|
||
</span><span class="boring">
|
||
</span><span class="boring">use trpl::Html;
|
||
</span><span class="boring">
|
||
</span>fn main() {
|
||
let args: Vec<String> = std::env::args().collect();
|
||
|
||
trpl::block_on(async {
|
||
let url = &args[1];
|
||
match page_title(url).await {
|
||
Some(title) => println!("The title for {url} was {title}"),
|
||
None => println!("{url} had no title"),
|
||
}
|
||
})
|
||
}
|
||
<span class="boring">
|
||
</span><span class="boring">async fn page_title(url: &str) -> Option<String> {
|
||
</span><span class="boring"> let response_text = trpl::get(url).await.text().await;
|
||
</span><span class="boring"> Html::parse(&response_text)
|
||
</span><span class="boring"> .select_first("title")
|
||
</span><span class="boring"> .map(|title| title.inner_html())
|
||
</span><span class="boring">}</span></code></pre>
|
||
<figcaption><a href="#listing-17-4">Listing 17-4</a>: Awaiting an async block with <code>trpl::block_on</code></figcaption>
|
||
</figure>
|
||
<p>When we run this code, we get the behavior we expected initially:</p>
|
||
<!-- manual-regeneration
|
||
cd listings/ch17-async-await/listing-17-04
|
||
cargo build # skip all the build noise
|
||
cargo run -- "https://www.rust-lang.org"
|
||
# copy the output here
|
||
-->
|
||
<pre><code class="language-console">$ cargo run -- "https://www.rust-lang.org"
|
||
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.05s
|
||
Running `target/debug/async_await 'https://www.rust-lang.org'`
|
||
The title for https://www.rust-lang.org was
|
||
Rust Programming Language
|
||
</code></pre>
|
||
<p>Phew—we finally have some working async code! But before we add the code to
|
||
race two sites against each other, let’s briefly turn our attention back to how
|
||
futures work.</p>
|
||
<p>Each <em>await point</em>—that is, every place where the code uses the <code>await</code>
|
||
keyword—represents a place where control is handed back to the runtime. To make
|
||
that work, Rust needs to keep track of the state involved in the async block so
|
||
that the runtime could kick off some other work and then come back when it’s
|
||
ready to try advancing the first one again. This is an invisible state machine,
|
||
as if you’d written an enum like this to save the current state at each await
|
||
point:</p>
|
||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">#![allow(unused)]
|
||
</span><span class="boring">fn main() {
|
||
</span><span class="boring">extern crate trpl; // required for mdbook test
|
||
</span><span class="boring">
|
||
</span>enum PageTitleFuture<'a> {
|
||
Initial { url: &'a str },
|
||
GetAwaitPoint { url: &'a str },
|
||
TextAwaitPoint { response: trpl::Response },
|
||
}
|
||
<span class="boring">}</span></code></pre>
|
||
<p>Writing the code to transition between each state by hand would be tedious and
|
||
error-prone, however, especially when you need to add more functionality and
|
||
more states to the code later. Fortunately, the Rust compiler creates and
|
||
manages the state machine data structures for async code automatically. The
|
||
normal borrowing and ownership rules around data structures all still apply,
|
||
and happily, the compiler also handles checking those for us and provides
|
||
useful error messages. We’ll work through a few of those later in the chapter.</p>
|
||
<p>Ultimately, something has to execute this state machine, and that something is
|
||
a runtime. (This is why you may come across mentions of <em>executors</em> when
|
||
looking into runtimes: an executor is the part of a runtime responsible for
|
||
executing the async code.)</p>
|
||
<p>Now you can see why the compiler stopped us from making <code>main</code> itself an async
|
||
function back in Listing 17-3. If <code>main</code> were an async function, something else
|
||
would need to manage the state machine for whatever future <code>main</code> returned, but
|
||
<code>main</code> is the starting point for the program! Instead, we called the
|
||
<code>trpl::block_on</code> function in <code>main</code> to set up a runtime and run the future
|
||
returned by the <code>async</code> block until it’s done.</p>
|
||
<section class="note" aria-role="note">
|
||
<p>Note: Some runtimes provide macros so you <em>can</em> write an async <code>main</code>
|
||
function. Those macros rewrite <code>async fn main() { ... }</code> to be a normal <code>fn main</code>, which does the same thing we did by hand in Listing 17-4: call a
|
||
function that runs a future to completion the way <code>trpl::block_on</code> does.</p>
|
||
</section>
|
||
<p>Now let’s put these pieces together and see how we can write concurrent code.</p>
|
||
<!-- Old headings. Do not remove or links may break. -->
|
||
<p><a id="racing-our-two-urls-against-each-other"></a></p>
|
||
<h3 id="racing-two-urls-against-each-other-concurrently"><a class="header" href="#racing-two-urls-against-each-other-concurrently">Racing Two URLs Against Each Other Concurrently</a></h3>
|
||
<p>In Listing 17-5, we call <code>page_title</code> with two different URLs passed in from the
|
||
command line and race them by selecting whichever future finishes first.</p>
|
||
<figure class="listing" id="listing-17-5">
|
||
<span class="file-name">Filename: src/main.rs</span>
|
||
<!-- should_panic,noplayground because mdbook does not pass args -->
|
||
<pre><code class="language-rust should_panic noplayground"><span class="boring">extern crate trpl; // required for mdbook test
|
||
</span><span class="boring">
|
||
</span>use trpl::{Either, Html};
|
||
|
||
fn main() {
|
||
let args: Vec<String> = std::env::args().collect();
|
||
|
||
trpl::block_on(async {
|
||
let title_fut_1 = page_title(&args[1]);
|
||
let title_fut_2 = page_title(&args[2]);
|
||
|
||
let (url, maybe_title) =
|
||
match trpl::select(title_fut_1, title_fut_2).await {
|
||
Either::Left(left) => left,
|
||
Either::Right(right) => right,
|
||
};
|
||
|
||
println!("{url} returned first");
|
||
match maybe_title {
|
||
Some(title) => println!("Its page title was: '{title}'"),
|
||
None => println!("It had no title."),
|
||
}
|
||
})
|
||
}
|
||
|
||
async fn page_title(url: &str) -> (&str, Option<String>) {
|
||
let response_text = trpl::get(url).await.text().await;
|
||
let title = Html::parse(&response_text)
|
||
.select_first("title")
|
||
.map(|title| title.inner_html());
|
||
(url, title)
|
||
}</code></pre>
|
||
<figcaption><a href="#listing-17-5">Listing 17-5</a>: Calling <code>page_title</code> for two URLs to see which returns first</figcaption>
|
||
</figure>
|
||
<p>We begin by calling <code>page_title</code> for each of the user-supplied URLs. We save
|
||
the resulting futures as <code>title_fut_1</code> and <code>title_fut_2</code>. Remember, these don’t
|
||
do anything yet, because futures are lazy and we haven’t yet awaited them. Then
|
||
we pass the futures to <code>trpl::select</code>, which returns a value to indicate which
|
||
of the futures passed to it finishes first.</p>
|
||
<section class="note" aria-role="note">
|
||
<p>Note: Under the hood, <code>trpl::select</code> is built on a more general <code>select</code>
|
||
function defined in the <code>futures</code> crate. The <code>futures</code> crate’s <code>select</code>
|
||
function can do a lot of things that the <code>trpl::select</code> function can’t, but
|
||
it also has some additional complexity that we can skip over for now.</p>
|
||
</section>
|
||
<p>Either future can legitimately “win,” so it doesn’t make sense to return a
|
||
<code>Result</code>. Instead, <code>trpl::select</code> returns a type we haven’t seen before,
|
||
<code>trpl::Either</code>. The <code>Either</code> type is somewhat similar to a <code>Result</code> in that it
|
||
has two cases. Unlike <code>Result</code>, though, there is no notion of success or
|
||
failure baked into <code>Either</code>. Instead, it uses <code>Left</code> and <code>Right</code> to indicate
|
||
“one or the other”:</p>
|
||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">#![allow(unused)]
|
||
</span><span class="boring">fn main() {
|
||
</span>enum Either<A, B> {
|
||
Left(A),
|
||
Right(B),
|
||
}
|
||
<span class="boring">}</span></code></pre>
|
||
<p>The <code>select</code> function returns <code>Left</code> with that future’s output if the first
|
||
argument wins, and <code>Right</code> with the second future argument’s output if <em>that</em>
|
||
one wins. This matches the order the arguments appear in when calling the
|
||
function: the first argument is to the left of the second argument.</p>
|
||
<p>We also update <code>page_title</code> to return the same URL passed in. That way, if the
|
||
page that returns first does not have a <code><title></code> we can resolve, we can still
|
||
print a meaningful message. With that information available, we wrap up by
|
||
updating our <code>println!</code> output to indicate both which URL finished first and
|
||
what, if any, the <code><title></code> is for the web page at that URL.</p>
|
||
<p>You have built a small working web scraper now! Pick a couple URLs and run the
|
||
command line tool. You may discover that some sites are consistently faster
|
||
than others, while in other cases the faster site varies from run to run. More
|
||
importantly, you’ve learned the basics of working with futures, so now we can
|
||
dig deeper into what we can do with async.</p>
|
||
<!-- TODO: map source link version to version of Rust? -->
|
||
</body>
|
||
</html>
|