rgoussu@goussu: ~/library/rust/compilers-and-runtimes
~/library/rust/compilers-and-runtimes cat async-runtimes.md

Async runtimes — Tokio, smol & the runtime-agnostic tension

# Rust ships async/await without an executor — Tokio as the de-facto standard, smol's composable alternative, embassy for embedded, and the unresolved runtime-agnostic library problem.

Conceptsaved 2026-08-09 #rust#async#tokio#runtime#concurrency

Overview

This is Rust's version of the runtime question the JVM answers with competing VMs and Go answers with one built-in scheduler: the language defines async fn and Future, but ships no executor, no reactor, no timers — a deliberate zero-cost stance that pushed the runtime into the library ecosystem. The market answered decisively: Tokio is the de-facto standard, the substrate under axum, tonic, sqlx, reqwest and most of the async ecosystem. The cost of that decisiveness is the ecosystem's most persistent tension: libraries in practice couple to a runtime, and writing runtime-agnostic async code remains genuinely hard.

Key points

  • What the language provides: Future as a compiled state machine, async/await syntax, Waker/Poll as the contract. What it doesn't: anything that runs a future — spawning, I/O event loops, timers, task scheduling all come from a crate.
  • Tokio is the default choice and the safe assumption: multi-threaded work-stealing scheduler, mio-based reactor (epoll/kqueue/IOCP), timers, tokio::sync primitives, #[tokio::main]/#[tokio::test] entry points, plus the ecosystem: tower, hyper, tracing, tokio-console. Production posture, LTS releases, corporate backing.
  • smol is the composable counter-design: a federation of small crates (async-executor, async-io, async-channel, blocking) you can assemble piecemeal — attractive for libraries and small tools; its pieces underlie several runtime-agnostic crates.
  • async-std is gone: officially discontinued in 2025 (its crates archived) — worth knowing because tutorials and older library feature flags still reference it; its ecosystem role folded into smol's crates and Tokio.
  • embassy brings async to embedded: executors and HALs for microcontrollers where async replaces an RTOS — a genuinely different answer unavailable to Go/Java.
  • The agnostic tension: a library doing I/O must pick a reactor, so "agnostic" libraries either abstract via traits + feature flags (sqlx's runtime-tokio, lapin), stick to executor-only needs (futures + async-channel), or just require Tokio (axum, tonic). There is no Executor trait in std, and the "keyword generics"/pluggable runtime work remains exploratory — assume Tokio unless you have a reason not to.
  • Async's sharp edges live here too: cancellation-by-drop, Send bounds on spawned tasks, blocking-the-executor hazards (spawn_blocking for CPU/blocking work), and observability via tokio-console and tracing.

Details

Comparison

Runtime Scheduler Reactor Sweet spot Ecosystem pull
Tokio Multi-threaded, work-stealing (current-thread optional) mio (epoll/kqueue/IOCP); tokio-uring experiment Network services, the default Dominant — axum, tonic, sqlx, reqwest assume it
smol async-executor, single or multi-thread async-io (polling crate) Small tools, composable libraries Its component crates outlive the umbrella
embassy Static, interrupt-driven executors HAL-integrated Embedded / no_std The embedded-async standard
glommio Thread-per-core, cooperative io_uring Storage/network engines wanting core affinity Niche, influential design

The Go/Java contrast, sharpened

Go bakes one scheduler into every binary and makes every function implicitly async-capable — no colored functions, no runtime choice, no zero-cost claim. Java (21+) retrofitted the same model as virtual threads on the one JVM. Rust chose explicit async with no runtime so that embedded targets, kernels and WASM could use the same language — and pays with the function-coloring split (sync vs async APIs) and the library-coupling problem above. All three are coherent answers; Rust's is the only one where "which runtime?" is a dependency-tree question — see Concurrency and Parallelism for the crossroads itself.

Related

Citations

[1] Tokio documentation [2] async-std discontinuation notice [3] Async Rust book — the state of async