- Published on
Why Does Rust Need Tokio for Async While .NET does not
- Authors

- Name
- Siddharth Singh
Why Tokio?
One question kept bothering me when I started learning Rust.
If Rust has async and await, why do I still need Tokio? In C#, I never had to think about this. I wrote an async method, awaited a few operations, and everything just worked. The runtime quietly handled the rest.
The difference isn't that .NET has better async support. The difference is that .NET made async the runtime's responsibility, while Rust made it the application's responsibility.
Take this C# code.
public async Task<string> DownloadAsync()
{
return await client.GetStringAsync("https://example.com");
}
When the HTTP request completes, the CLR already knows how to resume the method. It has a thread pool, a scheduler, timers, and integration with the operating system's asynchronous I/O. The runtime is constantly working behind the scenes, so developers rarely think about who is actually driving the asynchronous code.
Rust does something different.
async fn download() -> String {
read().await
}
This doesn't start running immediately. It simply creates a Future, which is nothing more than a description of work that can be performed later.
A Future is a bit like a movie on a DVD. The movie exists, but it won't start playing until you put it into a DVD player.
Tokio is that DVD player.
Its job is to repeatedly poll Futures, wait for the operating system to signal that network or file operations have completed, and resume execution at exactly the right point.
Rust could have bundled an async runtime into the language, but then every application—from tiny embedded devices to high-performance databases—would be forced to use the same scheduler and execution model. Instead, Rust standardizes the Future abstraction and lets applications choose how they want to execute it.
The Flexibility Shows Up in Real Systems
A great example of this philosophy is Apache Iggy. Earlier versions used Tokio, just like many Rust servers do. But as the project evolved, the team realized they wanted a completely different execution model—one built around a thread-per-core architecture, io_uring, and full control over scheduling. Tokio's work-stealing executor was no longer the best fit for their goals. Apache Iggy's migration to thread-per-core architecture
Instead of being locked into the runtime that came with the language, they evaluated several alternatives—monoio, glommio, and finally compio. They ultimately chose compio, not simply because it supports io_uring, but because its architecture separates the I/O driver from the executor. That meant they could reuse the io_uring driver while retaining the freedom to build an execution model tailored to a high-performance streaming system.
This is something that would be difficult if the language mandated a single async runtime.
Rust standardizes the Future abstraction, not the scheduler. That small design choice allows projects like Apache Iggy to move from a general-purpose runtime to one optimized for their workload without changing the language itself. The same flexibility is why Rust is becoming increasingly popular for databases, storage engines, messaging systems and other infrastructure software where the execution model is often just as important as the business logic.
References
If you'd like to explore the ideas discussed in this article, these are excellent starting points.
The Rust Programming Language – The official guide to Rust, including the chapters on asynchronous programming. https://doc.rust-lang.org/book/
Asynchronous Programming in Rust – The official async book explaining
Future,async/await, pinning and executors. https://rust-lang.github.io/async-book/The Tokio Documentation – Learn how Tokio's runtime, scheduler and reactor work. https://tokio.rs/
Rust
FutureTrait Documentation – The standard library documentation for theFutureabstraction. https://doc.rust-lang.org/std/future/trait.Future.htmlWithout Boats – Async Foundations – Excellent articles explaining the design decisions behind Rust's async model. https://without.boats/
Apache Iggy Engineering Blog – Thread-per-Core and io_uring – A practical example of why infrastructure software sometimes chooses a different execution model than Tokio. https://iggy.apache.org/blogs/2026/02/27/thread-per-core-io_uring/
Ryan Levick – Crust of Rust: Async/Await – One of the best deep dives into how Rust async works under the hood. https://www.youtube.com/watch?v=ThjvMReOXYM