Async Runtime

Before your non-blocking code can run, something needs to manage it, deciding which tasks run, when they run, and what to do while they are waiting. That manager is called a runtime.

Think of it like a coordinator at a call center; instead of one person sitting idle waiting for a call back, the coordinator keeps everyone busy by assigning new work while others wait.

Tokio is that runtime for Rust. Without it, async functions and .await do nothing on their own.

Macro

tokio-badge

The easiest way to start the Tokio runtime is with the tokio::main macro. Put it above your main function and it handles everything for you, starting the runtime, running your code, and shutting it down when done. It also lets main be non-blocking, so you can use .await directly inside it.

async fn fetch_network_request() -> u32 {
    89
}

#[tokio::main]
async fn main() {
    let result = fetch_network_request().await;
    assert_eq!(result, 89);
}

Add tokio to Cargo.toml with the macros and rt-multi-thread features enabled.

[dependencies]
tokio = { version = "*", features = ["macros", "rt-multi-thread"] }

Builder Approach

tokio-badge std-badge

tokio::main works well for most programs, but it makes all the decisions for you. If you need control over how the runtime is set up, use the Builder instead.

Think of Builder as a recipe. Each method you call adds an instruction, how many threads to use, what to call them, how much memory to give each one. Nothing actually happens until you call .build() at the end, which is when the runtime is created and ready to run.

In this example, the recipe sets up 4 worker threads, gives them the name "thread-one", and sets a stack size of 3MB each.

use std::io;
use tokio::runtime::Builder;

async fn fetch_network_request() -> u32 {
    89
}

fn main() -> io::Result<()> {
     let runtime = Builder::new_multi_thread()
        .worker_threads(4)
        .thread_name("thread-one")
        .thread_stack_size(3 * 1024 * 1024)
        .build()?;

    runtime.spawn(async {
        let result = fetch_network_request().await;
        assert_eq!(result, 89);
    });

    Ok(())
}

Add tokio to Cargo.toml with the rt-multi-thread feature enabled.

[dependencies]
tokio = { version = "*", features = ["rt-multi-thread"] }