liveBook · Manning

Tokio

As explained in Code Like a Pro in Rust

The Tokio runtime is the execution component of Tokio that schedules and runs asynchronous Rust tasks.

Tokio is an asynchronous runtime for Rust: it schedules and runs tasks built from futures. Rust supplies `Future`, `async`, and `.await`, but leaves runtime implementation to libraries. Tokio is a practical default because it is widely used and many async crates are designed around it. Choose a runtime early; mixing competing runtimes in one context is usually impractical. section 8.1

#[tokio::main]
async fn main() {
   use std::time;

   let duration = time::Duration::from_secs(1);

   tokio::time::sleep(duration).await;
   println!("Hello, world!");
}

`#[tokio::main]` starts the runtime and gives `main` an async context. The timer future runs when `.await` executes it; creating a future alone does not run it. section 8.3.1 section 8.4

Use the scheduler deliberately:

  • Await a future when its result is needed. An async block that is neither awaited nor spawned never executes. section 8.4
  • Use `tokio::task::spawn()` to launch a task. Await its `JoinHandle` when the task must finish before the program exits; an unawaited spawned task is not guaranteed to finish. section 8.4
  • Use `tokio::select!` or `tokio::join!` for explicit concurrency; spawned tasks can also introduce parallelism. section 8.11
use tokio::io::{self, AsyncWriteExt};
 
async fn write_file(filename: &str) -> io::Result<()> {                #1
    let mut f = tokio::fs::File::create(filename).await?;
    f.write(b"Hello, file!").await?;
    f.flush().await?;
 
    Ok(())
}
 
fn read_file(filename: &str) -> io::Result<String> {                   #2
    std::fs::read_to_string(filename)
}
 
#[tokio::main]
async fn main() -> io::Result<()> {
    let filename = "mixed-sync-async.txt";
    write_file(filename).await?;
 
    let contents =
        tokio::task::spawn_blocking(|| read_file(filename)).await??;   #3
 
    println!("File contents: {}", contents);
 
    tokio::fs::remove_file(filename).await?;
 
    Ok(())
}

For unavoidable synchronous I/O inside async code, run the blocking call with `tokio::task::spawn_blocking()` and await its result. Tokio runs that call on a managed blocking-thread queue; ensure its thread allocation suits the workload rather than blocking an async task. section 8.7

Async I/O is most useful when waiting dominates processing. Tokio lets you choose the number of worker threads, but async is not automatically faster when processing dominates. For production work, instrument tasks with `tracing`, inspect async behavior with `tokio-console`, and use Tokio’s testing macros to provide a runtime in tests. section 8.2 section 8.11

How Code Like a Pro in Rust covers it

How deep it goes
Demonstrates runtime setup, awaiting, task spawning, and blocking-call isolation without detailing scheduler internals.
Examples
Uses an awaited timer, a spawned-task completion pitfall, and a file operation mixing async and synchronous I/O.
What it assumes
Assumes familiarity with Rust functions while explaining how async contexts and futures execute on a runtime.
What you can do after
Apply it via Tokio to run futures, await required tasks, and isolate unavoidable blocking work.
How it connects
Connects futures and async/await to task scheduling, nonblocking I/O, concurrency, tracing, and async testing.

Also explained in

Compare how 3 books explain Tokio →

Related concepts

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