Stop Pretending Async Rust Can Replace Your RTOS

Your drone is falling. The motor controller has 50 microseconds to correct its attitude. The Rust async runtime is busy handling a queued HTTP request, because someone thought it was a good idea to run a web framework on a flight controller. You don’t need a type system right now. You need a scheduler that will drop everything and deal with the hardware.

That’s not a theoretical edge case. That’s the difference between cooperative multitasking and preemption. And it’s the reason why smart people are making a dangerous mistake in embedded systems.

Async Rust is beautiful. Memory safety. Fearless concurrency. Zero-cost futures. I get it. The web world embraced it, and for good reason. But somewhere along the way, we started pretending that a language pattern that works beautifully for handling millions of network requests also works for controlling physical machinery. It doesn’t. And the gap between those two worlds isn’t just frustrating — it’s catastrophic.

Concurrency is a story you tell your code. Preemption is a promise the hardware makes.

The core problem is embarrassingly simple: async Rust uses cooperative scheduling. Tasks yield at explicit points, and if a task doesn’t yield, nothing else runs. An RTOS, on the other hand, uses a preemptive scheduler. It can interrupt a running task at any instruction, based on priority. That’s not a small difference. That’s the entire ballgame.

When a real embedded engineer looks at an async Rust project in a hard real-time context, they see a logic analyzer showing missed deadlines. They see a scope that spikes beyond the worst-case interrupt latency. They see the difference between a system that responds to a sensor within microseconds and one that politely asks the current task if it wouldn’t mind pausing.

“But it works under light load,” people say. Yes. Loading a webpage works on a single thread with async. But your car’s airbag controller doesn’t have a “light load” mode. When the collision sensor fires, it has a fixed budget to deploy the airbag. If the CPU is busy calculating the physics of a non-collision, you’re dead. There’s no retry.

And here’s the part that gets lost in the hype: Safety from memory bugs doesn’t protect you from physics. Rust is brilliant at catching logic errors and memory errors at compile time. It cannot catch a scheduling error. It cannot guarantee that your interrupt handler runs within 10 microseconds. That’s not a compiler problem. That’s a systems architecture problem.

I’ve seen the benchmarks. The ones comparing async Rust with an RTOS. They test task switching speed and memory overhead. Guess what? Async is fast and small. That’s not the point. The point is worst-case interrupt latency under load. And almost nobody measures that, because it’s ugly. Because when you introduce any significant compute — encryption, signal processing, physics simulation — the cooperative model falls apart. The async runtime can’t preempt a long-running task. It can only wait.

Now, let me be fair. C isn’t safe. Manual memory management is a nightmare. We’ve all spent hours chasing a use-after-free that corrupted a message queue. The desire to get away from that is real. But the answer isn’t to import a web-paradigm into a machine that has to respect the laws of time. The answer is to make a preemptive RTOS that exploits Rust’s strengths without abandoning hardware guarantees.

Here’s the twist: Rust absolutely has a place in hard real-time systems. But not async Rust on top of an executor. Rust as the language inside the RTOS. A preemptive scheduler written in Rust, with memory safety at the kernel level, while still delivering deterministic preemption. That’s the future. That’s what we should be building. Not stuffing async/await into a device that needs to react to a falling drone.

So let’s stop pretending. Async Rust is not an RTOS. It’s a tool for I/O-bound problems, not interrupt-bound problems. If you put an airbag controller on async Rust, you’re betting your product’s lives on the hope that no task runs too long. That’s a bet no sane architect should make.

If you don’t know your worst-case interrupt latency, you don’t know if your product will kill someone.

Get a scope. Measure. And choose the right tool. C might be ugly, but at least it doesn’t lie to you about what’s running when the hardware needs attention. Rust is safer, but it’s not free. You still have to respect the physical world.

Stop pretending async is a drop-in. Start building a better RTOS in Rust. That’s the only way we get both safety and determinism.

FAQ

Q: Is async Rust ever acceptable in embedded systems?

A: Yes, but only for soft real-time systems with low CPU load and no hard interrupt deadlines. If you're building a thermostat that polls a sensor twice a second, async is fine. If you're controlling an airbag, a drone, or a medical device, you need preemption.

Q: What's the practical implication of this trade-off?

A: You must measure worst-case interrupt latency on actual hardware under maximum CPU load — not just task-switch benchmarks. If you can't guarantee the hard deadline, async Rust is the wrong architecture, regardless of how safe the compiler makes you feel.

Q: What's the contrarian take?

A: The real enemy isn't async Rust — it's the conflation of concurrency with preemption. Even C RTOSes can fail if you don't measure. But async makes it easier to deceive yourself because the logic is clean while the timing is not. The solution is to write a preemptive RTOS in Rust, combining memory safety with deterministic scheduling.

📎 Source: View Source