Last week, Carl Lerche—creator of Tokio—sat down with Vitaly Bragilevsky for a JetBrains livestream to celebrate Tokio's ten-year anniversary. Ten years. That's an eternity in Rust ecosystem time.

I've been writing async Rust for three years now. I've fought the borrow checker, debugged stuck tasks at 2 AM, and convinced myself I understood cooperative scheduling only to be humbled again. So when the creator of the most popular async runtime reflects on what he's learned, I pay attention.

Here's what stuck with me—and what you should actually care about.

The Thing Nobody Tells You About Cancellation

Here's what I always assumed: when you cancel a future, it just... stops. Clean exit, stack unwinding, done.

Wrong.

Cancellation in Rust works through Drop. When you drop a future, its cleanup logic runs. If that future owns a socket, the socket closes. But here's the trap: async functions can be dropped at any point—mid-await, between lines, anywhere.

"The biggest pitfall stems down to developers accidentally canceling something and not handling the cancellation appropriately."

This means you need to write defensively. If your async function holds a database connection and gets dropped mid-execution, that connection might sit in a weird state. The fix isn't to panic—it's to structure your code so cancellation is safe by default.

Why Your Tasks Are Stalling

This one bit me last month. I had an async function that did heavy JSON parsing in a loop—no .await calls inside—and wondered why my entire runtime ground to a halt.

Cooperative scheduling. Tasks in Tokio only yield at .await points. If you run CPU-intensive work without yielding, you block the entire scheduler.

The fix? Two options:

  1. Use tokio::task::spawn_blocking for CPU-heavy synchronous work
  2. Break up your work with periodic .await checkpoints

Tokio provides runtime metrics to detect this. If your tail latency is spiking and you don't know why, check whether you have tasks hogging the scheduler.

io_uring: Not the Silver Bullet You Think

I remember when io_uring hype was at its peak. "It's the future! 10x faster for I/O!"

Carl's take? For networking workloads, the real-world gains are often limited. It's more complex than epoll and has historically had more security issues. Tokio lets you mix in io_uring-specific crates when you have a clear use case—but for most web services, you won't notice the difference.

"I've not seen real performance benefits with swapping out io_uring for sockets under the hood in Tokio."

The lesson: don't chase benchmarks. Chase simplicity.

What Tokio Got Right

Looking back at Tokio's design philosophy:

They didn't reinvent scheduling patterns. They adopted proven strategies from Go and Erlang, including work-stealing schedulers. This is why Tokio won: not because it was revolutionary, but because it was reliable.

"There just wasn't a good reason to not use Tokio."

The Next Frontier: Toasty

Here's what excited me most: Carl is building Toasty—a higher-level web application framework for Rust.

Think about where we've been:

The missing piece isn't performance. It's ergonomics. We have async Rust, we have Tokio, but building a web app still feels harder than it should. Toasty aims to fix that—while still preserving escape hatches for advanced use cases.

This mirrors what I've seen in my own work. The ZeroClaw daemon started as low-level async infrastructure, but as it grew, I kept wanting higher-level abstractions. Not to hide complexity, but to stop repeating myself.

What This Means for You

If you're building async Rust in 2026:

  1. Understand cancellation — write defensive code that handles being dropped mid-execution
  2. Yield regularly — don't block the scheduler with CPU-heavy work
  3. Don't over-optimize — io_uring isn't your silver bullet
  4. Trust the defaults — Tokio's defaults are good for most use cases
  5. Watch Toasty — higher-level Rust web frameworks are coming

The ecosystem has matured. We're not figuring out if async Rust works anymore—we're figuring out how to make it productive. That's a different problem, and it's the right problem to have.

Ten years in, and async Rust is just getting interesting.