liveBook · Manning

Async runtime

As explained in Learn Rust in a Month of Lunches

An async runtime is software that schedules asynchronous tasks, drives Future polling, and determines when suspended computations are polled again.

An async runtime schedules asynchronous work and manages Future polling. When a Future is not ready, polling returns Poll::Pending; when it completes, polling returns Poll::Ready(value). The .await keyword asks the runtime to perform this process for you. section 19.3.2

A runtime becomes necessary when asynchronous code reaches the program boundary:

  • Place .await inside an async function or async block; ordinary functions cannot use it directly. section 19.3.2
  • Use a runtime to decide when suspended Futures should be polled again. section 19.3.2
  • In common Rust programs, add #[tokio::main] so Tokio manages the asynchronous main body. section 19.3.3

Running an async function

use tokio;\n\nasync fn async_give_8() -> u8 {\n 8\n}\n\n#[tokio::main]\nasync fn main() {\n let number = async_give_8().await;\n println!("{number}");\n}\n\nThe macro keeps main a regular program entry point while creating a runtime that blocks until the async body finishes. For network calls such as reqwest requests, each asynchronous operation is followed by .await. section 19.3.3

use tokio;
 
async fn async_give_8() -> u8 {
    8
}
 
fn main() {                                                   #1
    let body = async {                                        #2
        let some_number = async_give_8().await;
        let second_number = async_give_8().await;
        {
            ::std::io::_print(format_args!("{0}, {1}\n", some_number,
            second_number));
        };
    };
 
    {
        return tokio::runtime::Builder::new_multi_thread()    #3
            .enable_all()
            .build()
            .expect("Failed building the Runtime")
            .block_on(body);                                  #4
    }
}
#1 Look here—async fn is a lie! It’s actually just a regular fn main(). As far as Rust is concerned, the main() function is not async at all.
#2 First, everything gets enclosed inside a big async block called body. The .await keyword can be used inside here.
#3 Now the Tokio run time starts. It uses the builder pattern to set some configuration.
#4 And, finally, the part that matters: a method called block_on(). Tokio is actually just blocking until everything has been resolved!

Inspect the expanded form to see the async block, Tokio runtime builder, and block_on call hidden behind #[tokio::main]. section 19.3.3

How Learn Rust in a Month of Lunches covers it

How deep it goes
Explains Pending and Ready, await placement, macro behavior, and runtime expansion, but does not develop runtime internals.
Examples
Uses async_give_8, compiler errors, Tokio macro expansion, and a reqwest HTTP request.
What it assumes
Assumes basic Rust functions and return types while teaching Futures, polling, await, and async boundaries from the beginning.
What you can do after
Operate asynchronous Rust programs with Tokio, .await, async functions, and runtime-managed program entry points.
How it connects
Connects async functions, async blocks, Futures, polling, await, Tokio, and asynchronous HTTP requests.

Also explained in

Compare how 2 books explain async runtime →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Learn Rust in a Month of Lunches. The section links point to the parts of the book it draws on.