Rust's standard library defines the Future trait and the async/await syntax — but deliberately ships no async runtime. To actually run Futures, you bring a runtime crate. The dominant one is tokio.
Why no built-in runtime?
Different workloads want different schedulers — a web server, an embedded device, and a GUI app have different needs. So Rust standardizes the language feature (Futures, await) but leaves the executor to the ecosystem. It's the same philosophy as the rest of the language: a minimal core, with policy decisions left to libraries you choose.
tokio: the de facto runtime
tokio provides the executor that polls your Futures, plus async versions of I/O — TCP, files, timers, channels. The #[tokio::main] attribute turns your async fn main into a regular main that sets up the runtime and blocks on your top-level Future. From there, every .await inside runs on tokio's scheduler.
Future and await; tokio (or async-std, smol) gives you the executor that drives them and the async I/O to await on. This split is why you add tokio to Cargo.toml and annotate main with #[tokio::main] before any async actually runs.The ecosystem assumes tokio
Most async libraries — web frameworks like axum, database drivers like sqlx, HTTP clients like reqwest — are built on tokio. While alternatives exist, tokio is the safe default and what you'll encounter most. Most production Rust services run on it.