Async Rust: a gentle introduction
Tags: rust
Published: 2019-11-18 22:43:25

---
title: Async Rust: a gentle introduction
published: true
date: 2019-11-18 08:21:10 UTC
tags: rust
canonical_url: https://thomashartmann.dev/blog/async-rust
cover_image: https://thomashartmann.dev/images/async-rust.svg
---
<p>Hot off the heels of RustFest Barcelona and the stabilization of <code>async/.await</code>, I think it’s safe to say that one of Rust’s most anticipated language features has finally landed. And for that occasion (and because I’ve had some trouble understanding certain bits of it myself), I wanted to write a little introduction to asynchronous programming in Rust. We’ll be creating a super simple application that fetches some data from the internet using the our newfound <code>async</code> abilities. The stabilization of <code>async/.await</code> also coincides nicely with another event I’m excited about: This week saw the release of the most recent entries in the mainline Pokémon games, <a href="https://bulbapedia.bulbagarden.net/wiki/Pok%25C3%25A9mon_Sword_and_Shield">Pokémon Sword and Shield</a>, and because I’m a bit too busy to pick them up just yet, I’ll make do by fetching data from the <a href="https://pokeapi.co/">PokéAPI</a> for now.</p>
<p>I’m assuming some base knowledge of Rust’s syntax and ecosystem, but I hope that this is pretty accessible even to people very new to the community.</p>
<p>But before diving into the coding part, let’s cover some basic concepts of asynchronous programming and how it might be a bit different in Rust than what you’d expect.</p>
<h2 id="what-does-async-mean">What does <code>async</code> mean?</h2>
<p>In Rust, when we talk about <code>async</code>, we’re talking about running code concurrently, or having multiple overlapping (in time) computations run on a single thread. Multithreading is a related, but distinct concept. Multithreading is ideal for when you’ve got computationally intensive tasks (so-called <em>CPU-bound</em> tasks) that can be spread across multiple, separated cores. Concurrent programming is better suited for when the task spends a lot of time waiting, such as for a response from a server. These tasks are called <em>IO-bound</em>.</p>
<p>So asynchronous programming lets us run multiple of these IO-bound computations at the same time on a single thread. They can run at the same time because when they’re waiting for a response, they’re just idle, so we can let the computer keep working on something that isn’t waiting. When we reach a point where we need the result of an asynchronous computation, we must <code>.await</code> it. In Rust, values that are ‘awaitable’ are known as ‘futures’.</p>
<h2 id="rusty-weirdness">Rusty weirdness</h2>
<p><code>async</code> in Rust may be a bit different from what you’re used to in other languages. Having done asynchronous coding mostly in JavaScript and C#, it certainly was to me. Here’s a few key things to understand:</p>
<h3 id="an-async-function-does-not-necessarily-start-executing-immediately">An <code>async</code> function does not (necessarily) start executing immediately</h3>
<p>To start an asynchronous function, you must either <code>.await</code> it or launch a task using an <em>executor</em> (we’ll get to that in a moment). Until this happens, all you have is a <code>Future</code> that has not started. Let’s look at an example to make it clearer:</p>
```rust
use async_std::task;
// ^ we need this for task spawning

async fn negate_async(n: i32) -> i32 {
    println!("Negating {}", n);
    task::sleep(std::time::Duration::from_secs(5)).await;
    println!("Finished sleeping for {}!", n);
    n * -1
}

async fn f() -> i32 {
    let neg = negate_async(1);
    // ... nothing happens yet
    let neg_task = task::spawn(negate_async(2));
    // ^ this task /is/ started
    task::sleep(std::time::Duration::from_secs(1)).await;
    // we sleep for effect.

    neg.await + neg_task.await
    // ^ this starts the first task `neg`
    // and waits for both tasks to finish
}
```
<p>So in the above little code snippet, here’s what’s going on.</p>
<ul>
<li>The first line imports <code>async_std::task</code>. There’s more on this below, but we need an external library to run futures as the standard library does not come with an <em>executor</em>.</li>
<li>The async function <code>negate_async</code> takes as input a signed integer, sleeps for 5 seconds, and returns the negated version of that integer.</li>
<li>The async function <code>f</code> is more interesting:
<ul>
<li>The first line (<code>let neg ...</code>) creates a <code>Future</code> of the <code>negate_async</code> function and assigns it to the <code>neg</code> variable. <strong>Importantly, it does <em>not</em> start executing yet.</strong>
</li>
<li>The next line of code (<code>let neg_task ...</code>) uses the <code>task::spawn</code> function to <strong>start executing</strong> the <code>Future</code> returned by <code>negate_async</code>. Like with <code>neg</code>, the <code>Future</code> returned by <code>negate_async</code> is assigned to the <code>neg_task</code> variable.</li>
<li>Next: we sleep for a second. This is so that it will be obvious from the output when a task starts running.</li>
<li>Finally, we await both futures, add them together, and return them. By awaiting <code>neg</code>, we start executing the <code>Future</code> and run it to completion. Since <code>neg_task</code> has already been started, we just wait for it to finish.</li>
</ul>
<p>So what’s the result of this, then?</p>
```sh
Negating 2
# <- there's a 1 second pause here
Negating 1
Finished sleeping for 2!
Finished sleeping for 1!
```
<p>As we can see, the second future, <code>neg_task</code>, started executing as soon as it was called—thanks to <code>task::spawn</code>—while <code>neg</code> did not start executing until it was awaited.</p>
</li>
</ul>
<h3 id="you-need-an-external-library-to-use-async.await">You need an external library to use <code>async/.await</code>
</h3>
<p>As was briefly alluded to above, you need to reach for an external library to do asynchronous programming in Rust. This took me a while to understand, as I’m used to it being part of the language experience. In Rust, however, <strong>you need a dedicated <em>executor</em></strong><a href="#fn1" id="fnref1"><sup>1</sup></a>. The executor is what takes care of <em>executing</em> the futures, polling them and returning the results when they’re done. The standard library does not come with an executor, so we need to reach out to an external crate for this. There are a few ones to choose from, but the two most prominent ones are <a href="https://async.rs/"><code>async-std</code></a> (which we’re using here) and <a href="https://tokio.rs/"><code>tokio</code></a>.</p>
<h2 id="a-minimal-async-example">A minimal async example!</h2>
<p>Alright, let’s get practical. This is the reason that I’m writing this post. As mentioned at the start, we’ll be creating a super simple application that fetches some Pokémon data and prints it to the console. For preparation, make sure you’ve got at least version 1.39 of Rust and cargo available.</p>
<h3 id="step-1-creating-the-application">Step 1: creating the application</h3>
<p>Let’s create a new application! Simply run this command in your preferred directory:</p>
```sh
cargo new async-basics
```
<h3 id="step-2-dependencies">Step 2: Dependencies</h3>
<p>We’re going to be using <a href="https://crates.io/crates/async-std"><code>async-std</code></a> for spawning tasks, and <a href="https://crates.io/crates/surf"><code>surf</code></a> to fetch data from the API. Let’s add them to the Cargo.toml file. Your whole file should look something like this:</p>
```toml
[package]
name = "async-basics"
version = "0.1.0"
authors = ["Your Name <your.email@provider.tld>"]
edition = "2018"

[dependencies]
async-std = "1"
surf = "1"
```

<p>Nice! This is going swimmingly!</p>
<h3 id="step-3-fetch-data">Step 3: Fetch data</h3>
<p>Okay, final step. Let’s modify the <code>main.rs</code> file. We’ll make it as simple as possible. Here’s what we want to use:</p>
```rust
use async_std::task;
use surf;

// fetch data from a url and return the results as a string.
// if an error occurs, return the error.
async fn fetch(url: &str) -> Result<String, surf::Exception> {
    surf::get(url).recv_string().await
}

// execute the fetch function and print the results
async fn execute() {
    match fetch("https://pokeapi.co/api/v2/move/surf").await {
        Ok(s) => println!("Fetched results: {:#?}", s),
        Err(e) => println!("Got an error: {:?}", e),
    };
}

fn main() {
    task::block_on(execute());
    // ^ start the future and wait for it to finish
}
```
<p>That’s all the code you need. In fact, it’s <em>more</em> than what you need, as some parts have been broken up for legibility. Let’s walk through it!</p>
<dl>
<dt><code>use</code> statements</dt>
<dd>Nothing exciting here. Just importing the crates we declared in the Cargo.toml file: <code>surf</code> and <code>async_std</code>.
</dd>
<dt><code>fetch</code></dt>
<dd>This is simply a thin wrapper around the <code>surf::get</code> function which returns either the payload as a <code>String</code> or an <code>Exception</code> if something went wrong.
</dd>
<dt><code>execute</code></dt>
<dd>This function calls fetch with the endpoint for the move <code>Surf</code>, waits for the result to return, and then matches on the result. If everything went well: print the output. Else: print the error.
</dd>
<dt><code>main</code></dt>
<dd>
<code>main</code> simply kicks off <code>execute</code> and waits for it to finish. <code>task::block_on</code> is a synchronous counterpart to <code>task::spawn</code> that starts an asynchronous operation, but blocks until it has finished. Because the <code>main</code> function can’t itself be <code>async</code> (at least not at the time of writing), we can’t use <code>.await</code> in it, but we <em>can</em> block on asynchronous operations.
</dd>
</dl>
<h3 id="step-4-extend-it">Step 4: Extend it!</h3>
<p>Hey, you made it this far; congrats! That’s all I really have in store for you for this one, but if you want to play around a bit more, how about adding <a href="https://crates.io/crates/serde"><code>serde</code></a> and try using <code>surf</code>’s <code>recv_json&lt;T&gt;</code> instead? If you’d rather keep looking at <code>async/.await</code>, how about performing multiple requests simultaneously? Or how about making a PokéAPI CLI? (<em>Ooh, that sounds like fun! Hit me up if you’re doing this; I want in!</em>)</p>
<h2 id="parting-words-and-resources">Parting words and resources</h2>
<p>So there you have it, dear reader. I hope you have found this useful. <code>async/.await</code> is finally stabilized and it feels like we’ve taken a major leap forward. I’m very much looking forward to seeing what happens in the coming months and what the community makes of this.</p>
<p>If you’re looking for more resources on async Rust, be sure to check out the <a href="https://rust-lang.github.io/async-book/index.html">Async Book</a>. I also recommend the <a href="https://book.async.rs/">async-std book</a> for some extra insights.</p>
<p>Until next time: take care!</p>
<h2 id="footnotes">Footnotes</h2>
<section>
<hr>
<ol>
<li id="fn1"><p>See <a href="https://www.reddit.com/r/rust/comments/dsy6ax/announcing_rust_1390/f6st41v?utm_source=share&amp;utm_medium=web2x">this insightful Reddit comment thread</a> for more on this.<a href="#fnref1">↩</a></p></li>
</ol>