Asynchronous Rust: basic concepts
Tags: rust, beginners, programming, learning
Published: 2021-08-29 20:56:01

> _TL;DR: I will try to give an easy-to-understand account of some concepts surrounding asynchronous Rust: async, await, Future, Poll, Context, Waker, Executor and Reactor._

As with most things I write here, we already have good content related to asynchronous Rust. Let me mention a few:
- The [_Asynchronous Programming in Rust_](https://rust-lang.github.io/async-book), a.k.a. _async book_; incomplete, but great.
- Steve's talks on [Rust's Journey to async/await](https://www.youtube.com/watch?v=lJ3NC-R3gSI) and on [how it works](https://www.youtube.com/watch?v=NNwK5ZPAJCk).
- Without Boats' [proposal for await syntax](https://without.boats/blog/await-decision/) (the other entries with the tags `async` and `Future` are also excellent).
- Jon's stream on [how Futures and async/await works](https://www.youtube.com/watch?v=9_3krAQtD2k).

With this amount of superb information, why writing about it? My answer here is the same for ~~almost~~ every other entry on my DEV blog: to reach an audience for which this content is still a bit too hard to grasp.

So, if you want something in a more intermediary level, go straight to the content listed above. Otherwise, let's go :)

---

## async/.await
Asynchronous Rust (async Rust, for short) is delivered through the `async/.await` syntax. It means that these two keywords (`async` and `.await`) are the centerpieces of writing async Rust. But what is async Rust?

The _async book_ states that async is a _concurrent programming model_. Concurrent means that different tasks will perform their activities alternatively; e.g., task A does a bit of work, hands the thread over to task B, who works a little and give it back, etc.

> _Do not confuse it with parallel programming, where different tasks are running_ simultaneously. _You **can** combine concurrent and parallel programmin (e.g., by spawning futures), but I will not cover it here since `async/.await` is used to enable_ concurrent _programming, so that is my focus here._

In short, we use the `async` keyword to tell Rust that a block or a function is going to be asynchronous.
```rust
// asynchronous block
async {
    // ...
}

// asynchronous function
async fn foo(){
    // ...
}
```

But what does it mean **for a Rust program** to be asynchronous? It means that it will return an implementation of the `Future` trait. I will cover `Future` in the next section; for now, it is enough to say that a `Future` represents a value that may or may not be ready.

We handle a `Future` that is returned by an async block/function with the `.await` keyword. Consider the silly example below:
```rust
async fn foo() -> i32 {
    11
}

fn bar() {
    let x = foo();
    
    // it is possible to .await only inside async fn or block
    async {
        let y = foo().await;
    };
}
```

In this case, `x` is not `i32`, but the implementation of the `Future` trait (`impl Future<Output = i32>` in this case). The variable `y` on the other hand, will be a `i32`: **11**.

Other way to visualize this is to understand that Rust will _desugar_ this
```rust
async fn foo() -> i32 {}
```
into something like this
```rust
fn foo() -> impl Future<Output=i32>{}
```

Of course, there is no asynchronous anything happening here. But if `foo()` was complex, having to wait for [`Mutex`](https://dev.to/rogertorres/smart-pointers-in-rust-what-why-and-how-oma) locks or is listening to a network connection, instead of holding the thread for the whole time, Rust would do as much progress as possible on `foo()` and then yields the thread to do something else, taking it back when it could do more work. 

Hopefully, it will make sense after we go through concepts like `Future`, `Poll` and `Wake`. For now, it is enough that you have a general idea of the use of both `async` and `await`.

> _Be sure to read the [async/.await Primer](https://rust-lang.github.io/async-book/01_getting_started/04_async_await_primer.html)._

---

## Futures
I think it is not an exaggeration to say that the `Future` trait is the heart of async Rust. 

A `Future` is a trait that has:
- An `Output` type (`i32` in the example above).
- A `poll` function.

`poll()` is a function that does as much work as it can, and then returns an enum called `Poll`:
```rust
enum Poll<T> {
    Ready(T),
    Pending,
}
```

As you can see, my description of `.await` and `poll()` kind of overlap. That's because calling `.await` will eventually call `poll()`. More on this later.

This enum is the representation of what I wrote earlier, that a Future represents a value that may or may not be ready.

The general idea behind this function is simple: when someone calls `poll()` on a future, if it went all the way through completion, it returns `Ready(T)` and the `.await` will return `T`. Otherwise, it will return `Pending`.

The question is, if it returns `Pending`, how do we get back at it, so it can keep working towards completion? The short answer is the _reactor_. However, we have some ground to cover before getting there.

---

## Poll, Context, Waker, Executor and Reactor
**Lots of words!** But I honestly think it is easier to bundle everything together because it is easier to understand what they do in context. And to illustrate this, I came up with a simplified hypothetical scenario.

Suppose we have a `Future` created via `async` keyword. Let's remember what a Future is:
```rust
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub trait Future {
    type Output;
    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
}
```

> _I will not cover_ `Pin` _here, as it is somewhat complex and not necessary to understand what is going on here._

As hinted by the code above, futures in Rust are lazy, which means that just declaring them will not make them run.

Now, let's say we run the future using `.await`. "Run" here means delivering it to an "executor" that will call `poll()` in the future. 

But what is the executor? Oversimplifying, it is a schedule algorithm that will actually poll the futures. So, when you call `.await`, who are going to do the work is an executor.

Ok, we called `.await`, the future was polled and returned `Ready<T>`. What happens? The `.await` will return `T` and the executor will get rid of the future, so it does not get polled again.

Alternatively, if the polled future wasn't able to do all the work, it will return `Pending`. 

After receiving `Pending`, the executor will not poll the future again until it is told so. And who is going to tell him? The "reactor". It will call the `wake()` function on the `Waker` that was passed as an argument in the `poll()` function. That allows the executor to know that the associated task is ready to move on. 

But what is the reactor? It is the executor's brother. While the executor is on the Olympus, managing things, listening to ~~prayers~~ `.await`s, the reactor is on the Hades, working alongside the system I/O, doing the heavy lifting. It is the reactor that will know the best time to `poll` that future again, and it will do so calling `wake()`.

So, should you, just starting to read Rust async stuff, worry about how executor and the reactor work behind the scene? Not really. Why? Because when we talk about executor and reactor we are already talking about runtimes; and when we talk about runtimes we are usually talking about [Tokio](https://tokio.rs/). In fact, calling it by the names [executor](https://docs.rs/tokio/0.1.22/tokio/executor/index.html) and [reactor](https://docs.rs/tokio/0.1.22/tokio/reactor/index.html) is already adhering to Tokio nomenclatures. So, at the end, all you have to do is incorporate Tokio on your project. The usual way to do this is using its procedural macro before the `main` function:
```rust
#[tokio::main]
async fn main(){
  // your async code
}
```

> Still about the reactor, [Jon spent 45 minutes explaining this while drawing on a blackboard,](https://www.youtube.com/watch?v=9_3krAQtD2k&t=3004s) and I will not pretend I can do a better job. So, if you want to dive into this level of detail, check the link above.

---

## Wrapping up 
Let us recap:
- `async` is used to create an asynchronous block or function, making it return a `Future`.
- `.await` will wait for the completion of the future and eventually give back the value (or an error, which is why it is common to use the [question mark operator](https://doc.rust-lang.org/reference/expressions/operator-expr.html#the-question-mark-operator) in `.await?`).
- `Future` is the representation of an asynchronous computation, a value that may or may not be ready, something that is represented by the variants of the `Poll` enum.
- `Poll` is the enum returned by a future, whose variants can be either `Ready<T>` or `Pending`.
- `poll()` is the function that works the future towards its completion. It receives a `Context` as a parameter and is called by the executor.
- `Context` is a wrapper for `Waker`.
- `Waker` is a type that contains a `wake()` function that will be called by the **reactor**, telling the **executor** that it may poll the future again.
- **Executor** is a scheduler that executes the futures by calling `poll()` repeatedly.
- **Reactor** is something like an event loop responsible for waking up the pending futures.

---

Ok, there is certainly more to talk about, such as the `Send` and `Sync` traits, `Pinning` and so on, but I think that, for a beginner post, we had enough.

See you next time!

> Cover art by [TK](https://unsplash.com/photos/9AxFJaNySB8).

---

> _**Edit — Sep, 1st, 2021**: I made some changes, as I realized my effort to simplify some things made them sound just wrong. This problem might still haunt the text here and there, so if you read something where I sacrificed correctness in favor of simplicity, **please** call me out._