Rust’s 2026 async runtime myths vs facts
Explore the evolving Rust async landscape where Tokio dominates with 150 million monthly downloads. Learn why async-std was discontinued in March 2025 and compare performance metrics between Tokio, smol, and glommio for production services.
The 2026 async ecosystem
Tokio dominates the async ecosystem. It has 150 million monthly downloads and supports 20,768 crates. Most major production crates, including axum, reqwest, sqlx, and tonic, require Tokio to function. This dominance creates a massive ecosystem lock-in. Because most major crates like axum, reqwest, sqlx, and tonic require Tokio, choosing a different runtime often leads to a situation where you must pull in a second executor to satisfy dependencies. If you select async-std and then attempt to use reqwest, you pull in a hidden Tokio runtime that consumes extra memory and adds complexity to your build process. Many teams find that managing these two competing executors is a logistical headache that prevents efficient development.
Libraries still need to be written against individual runtimes. This makes the ecosystem feel fragmented. If you use a library built on async-std, such as surf, you face difficulties when trying to integrate it with a Tokio-based project. This coupling remains a major hurdle for the Rust community.
async-std is discontinued. The project officially sunset async-std on March 1, 2025. It previously tried to mirror the Rust standard library, but the project failed to create a runtime that was fully compatible with all standard library expectations. The community now recommends smol as the primary alternative for those who prefer a simpler API.
Performance myths and complexity
Misconceptions about async performance cause service failures. Developers often believe the async/await syntax itself makes code faster. This is incorrect. Async/await only compiles to state machines. The actual speed depends on the executor.
Blocking code is a primary killer of performance. When engineers attempt to run heavy computation directly inside async tasks, they create blocking problems that manifest as unpredictable latency spikes when the service is under significant load. This can ruin the performance of an entire network of services. If you call std::fs::read instead of tokio::fs::read, you starve the executor. You must use tokio::task::spawn_blocking to move CPU-bound work to a separate thread pool.
Tokio’s multi-threaded runtime relies on a work-stealing scheduler. The multi-thread scheduler executes futures on a thread pool, using a work-stealing strategy that distributes tasks across worker threads to ensure high throughput for web services. If a thread becomes idle, it steals tasks from the local queue of a busy thread. Because tasks in multi-threaded runtimes may outlive the scope they were created in, ensuring that references remain valid for the duration of the task’s execution remains a fundamental challenge for any developer working in Rust. This design forces you to use synchronization primitives like Arc and Mutex for most shared data. Some developers call this requirement the "original sin" of async programming.
While a single-threaded runtime can handle an infinite amount of work if the application is 100% I/O-bound, real-world applications always perform some CPU work to process data. Multi-threaded execution allows multiple CPU cores to share this load, but it introduces overhead through synchronization and scheduler complexity.
Can a single-threaded runtime provide enough throughput for high-concurrency networking?
Selecting the correct runtime
The choice depends on your specific deployment environment. You already know how to manage your Cargo.toml files.
| Runtime | Use Case | IO Model |
|---|---|---|
| Tokio | Web servers and APIs | epoll/kqueue/IOCP |
| async-std | Legacy projects | epoll/kqueue |
| smol | Minimal binaries | epoll/kqueue |
| glommio | High-throughput Linux | io_uring |
| Runtime | Requests/sec | P50 Latency | P99 Latency | Memory |
|---|---|---|---|---|
| Tokio | 1.2M | 0.8ms | 4.2ms | 85MB |
| async-std | 900K | 1.1ms | 6.8ms | 92MB |
| smol | 1.0M | 0.9ms | 5.1ms | 45MB |
| glommio | 1.8M | 0.4ms | 1.2ms | 120MB |
Tokio provides the best results for backend services and web APIs. It scales to handle high-connection-count servers. Its work-stealing model handles mixed I/O and CPU workloads.
smol is a great choice for specialized or embedded projects. The entire executor contains roughly 1,000 lines of code. It prioritizes composability.
glommio is the best choice for Linux-based infrastructure. It requires Linux kernel 5.8 or higher. It uses a thread-per-core model and io_uring to bypass kernel syscalls in the hot path. This provides deterministic tail latency for throughput-critical services like databases or proxies.
Use Tokio.