My Authors
Read all threads
So here's an opinion thread that should probably be a blog post:

If you're building an embedded system: you probably DON'T need an RTOS. In fact, I think using one is usually an anti-pattern. At least, when using #embedded @rustlang.

Yes, even for hard real-time systems.

One of the first things I see people asking when moving from C to @rustembedded is "what RTOS can I use in Rust"?

I think this is the XY problem: people ask/reach for an RTOS because that was the only thing that made sense for any reasonably complex system in C!


Building embedded systems in C from scratch means basically no "batteries included". This includes no standard:

* build system
* package manager
* docs tooling
* reusable libraries/components
* safe concurrency primitives

Oh, and no HAL/BSP!

So where do you get these if you're building a system?

Well, sometimes your chip manufacturer provides some of these, to some varying level of quality.

If you want an ecosystem that works across more than one chip/family, what do you do?

Right now, the only pragmatic option in C is to opt-in to one of the RTOS ecosystems. Zephyr, FreeRTOS, etc. end up being the only "meeting place" for embedded C developers to have a consistent set of any of these tools!

I'd go as far to say: the human/software ecosystem available around RTOS communities is FAR more valuable than whatever features your RTOS provides

But does that mean that your embedded systems needs multiple threads because you like the ecosystem that comes with them?

Building an RTOS based system is still hard, even with the docs and tools provided.

You need to appropriately allocate stack/other resources. You need to think about how the tasks interact. You STILL need to think about lifetimes and memory safety, even with an MPU!

Sure, the RTOS might be helpful enough to kill your task if you do make a mistake, but that's at runtime! You're also paying for all of the overhead of the kernel itself, even if you don't really need the concurrency of an RTOS, you just want logical partitioning of code!

So, if not an RTOS, then what? At least in Rust?

Well, most of Rust's tooling and "batteries" work just fine for microcontroller targets!

* Compiler toolchain: Cross compile out of the box
* Package Manager: Cargo, same as desktop targets
* Docs/testing: Yup, still Cargo

* Package Library:, same as desktop targets

It's actually EASIER to share code across desktop and embedded targets with Rust, because if a crate is marked `#[no_std]`, it almost certainly will work on either platform, no hacks required!

Oh, also the borrow checker and strong types? Even more relevant, even with no heap.

Interrupt routines are basically threads: They run concurrently (when invoked), so even Send and Sync make sense to have the compiler enforce! This means errors are caught at compile time.

If you're not hard real-time, or don't need maximal concurrency, you're good to go! You can just write a simple main-loop, and not worry about complexity or concurrency at all! You'll still get modern tooling, compile time mem safety, and more.

But what if you need concurrency? I *still* don't think you need an RTOS, at least not most of the time!

Tools like, which is a concurrency framework, allows you to schedule tasks based on interrupts. You can use the NVIC as a hardware scheduler!

With RTFM, you can develop hard real-time systems, often with even stronger or simpler to prove guarantees than if you are using an RTOS based system!

All while writing code that feels idiomatic to Rust, regardless of whether it's embedded or not.

At @FerrousSystems, we're also exploring how things like async/await can be used to write concurrent, linear code, for cooperative multitasking, without having multiple stacks (see…).

You still get all the awesome compile time safety!

If you really need multiple tasks that are:

Mutually distrustful
Entire applications (not just libraries) need to run unmodified on different targets
Fallible on a per-task level

Then you'll probably want an RTOS like @talkingtock! But this is a pretty specific domain.

At the end of the day, most people are building a single application, with a little concurrency, that is intended to run on a single target. You want a nice experience, reusable code, and be as confident as possible in what you're shipping.

I think @rustlang is perfect for that.
Here's my sales plug: If you're interested in @rustlang (on or off of #embedded systems), reach out to us at @FerrousSystems.

We've helped folks move production embedded systems to Rust, and are happy to evaluate, help, train, or advise your company if you need assistance.
Missing some Tweet in this thread? You can try to force a refresh.

Enjoying this thread?

Keep Current with James Munns

Profile picture

Stay in touch and get notified when new unrolls are available from this author!

Read all threads

This Thread may be Removed Anytime!

Twitter may remove this content at anytime, convert it as a PDF, save and print for later use!

Try unrolling a thread yourself!

how to unroll video

1) Follow Thread Reader App on Twitter so you can easily mention us!

2) Go to a Twitter thread (series of Tweets by the same owner) and mention us with a keyword "unroll" @threadreaderapp unroll

You can practice here first or read more on our help page!

Follow Us on Twitter!

Did Thread Reader help you today?

Support us! We are indie developers!

This site is made by just three indie developers on a laptop doing marketing, support and development! Read more about the story.

Become a Premium Member ($3.00/month or $30.00/year) and get exclusive features!

Become Premium

Too expensive? Make a small donation by buying us coffee ($5) or help with server cost ($10)

Donate via Paypal Become our Patreon

Thank you for your support!