Embedded Rust Workshop: Reflections from Hackaday Supercon

Every November, hundreds of engineers, makers, and hardware hackers converge in Pasadena, California for Hackaday’s annual Supercon, a three-day celebration of open hardware, creative problem-solving, tinkering, and passion projects. It’s part conference, part reunion, and part art exhibit. Supercon has always been about sharing knowledge, so this year I decided to contribute something new: an Intro to Embedded Rust Workshop.

Rust is one of those languages that inspires strong opinions. Some developers swear by its safety guarantees and modern syntax; others find it unnecessarily rigid or slow to work with. My goal wasn’t to convince anyone either way. Rather, I wanted to give people a real, hands-on experience running Rust on actual hardware. If you’ve ever wondered whether Rust might be a good fit for your embedded projects, I wanted this workshop to help you decide for yourself.

Note that you can find the entire workshop, including a written tutorial should you want to try it on your own time, here: https://github.com/ShawnHymel/workshop-embedded-rust

I want to give special thanks to DigiKey for sponsoring the workshop and providing attendees with hardware kits. Be on the lookout for a full 12-part video series on embedded Rust coming soon! Subscribe to DigiKey’s YouTube channel if you want to be notified of new releases.

Why Rust for Embedded Systems?

Rust is an interesting language. It’s a systems language designed to deliver C-like performance while enforcing strict memory safety rules at compile time. For embedded engineers, those are compelling promises: deterministic performance without the risk of memory leaks, dangling pointers, or data races.

Traditionally, we’ve had two choices for managing memory. At one end are manual memory management languages like C and C++, which give you total control but also total responsibility. Forget a free() call or dereference a null pointer, and you can bring down the system (or worse: ship a bug that only appears in the field). At the other end are garbage-collected languages like Python or Java, which handle allocation automatically but at the cost of unpredictability and overhead.

Rust takes a third path. Its ownership model enforces memory safety at compile time through a set of rules about who owns what piece of data and how long it lives. If you try to use a variable after it’s been moved, or create a data race by mutating a shared state, the compiler stops you before the code ever runs. There’s no garbage collector, and no manual freeing of memory. Rather, the compiler inserts the necessary cleanup code deterministically when objects go out of scope.

This makes Rust particularly appealing for embedded work, where we want tight control of hardware and predictable execution without giving up safety. It also introduces a learning curve. The borrow checker (the system that enforces these ownership rules) is famously strict, and it can feel like you’re arguing with the compiler at first. But once you start understanding why it’s complaining, the errors begin to make sense. Rust pushes developers toward writing safe, explicit code that the compiler can reason about.

Of course, safety and speed aren’t the only concerns. Embedded Rust also benefits from a modern toolchain: Cargo handles building, dependency management, and packaging far more elegantly than Makefiles. Rust’s ecosystem of crates (packages) grows daily, and the embedded community has been building strong hardware abstraction layers (HALs) for popular microcontrollers like STM32, ESP32, and Raspberry Pi’s RP2040/2350 family.

Still, Rust isn’t a silver bullet. Compile times are longer, the language is more verbose, and many silicon vendors have yet to provide official support. There’s also no mature real-time operating system built in Rust. Frameworks like Embassy and RTIC are promising but still evolving. The ecosystem is young, which makes exploration both exciting and occasionally frustrating.

A Hands-On Test Drive at Supercon

For the workshop, I wanted attendees to experience all of this firsthand. Each participant received a Raspberry Pi Pico 2, a breadboard, an LED and resistor, a pushbutton, and a TMP102 temperature sensor. The entire environment ran inside a Docker image that I’d pre-built with Rust, Cargo, and the RP2350 toolchain. This way, everyone (regardless of host operating system) had the same setup and could focus on learning Rust instead of troubleshooting toolchains.

We started simple, with the classic blinky example. Building that first program in Rust shows just how much happens under the hood: linker scripts, memory maps, HAL initialization, and type-safe abstractions for GPIO. The first time the LED blinked, there was an audible wave of relief across the room.

Next, we paused to explore Rust’s ownership and borrowing rules. It’s one thing to read about them in the Rust Book; it’s another to see the compiler catch something that would have caused undefined behavior in C.

From there, we moved on to a more complex project: reading temperature data over I2C from the TMP102 sensor. This exercise built on the Blinky example but added heapless strings, result handling, and a simple USB serial interface. We even discussed how the I2C peripheral was “taken” by Rust’s ownership system, preventing other parts of the program from modifying it simultaneously.

As the two-hour session unfolded, the room became a mix of excitement and concentrated frustration, which is a good balance I like to see in workshops. Some participants appreciated Rust’s explicitness while others missed the flexibility and familiarity of C (and said as much on their way out).. A few managed to add optional challenges like button debouncing or LED wrappers using generics and traits.

For me, that was the goal. Everyone left with a working set of example code, a Docker environment they could recreate later, and enough familiarity to decide whether Rust fit their future projects. Hands-on experience was the goal, not evangelism.

Limitations

Rust evoked a mix of excitement and skepticism. On the positive side, memory safety without garbage collection is a genuine breakthrough. The language makes concurrency less terrifying: threads, interrupts, and shared resources can be modeled safely using the type system. And thanks to Cargo, reproducible builds and dependency management are almost effortless compared to traditional embedded C workflows.

The performance is impressive too. Compiled Rust often matches or beats equivalent C in runtime benchmarks, and its abstractions truly are “zero-cost.” For example, you can define a generic function that works across multiple data types without paying for dynamic dispatch at runtime. The compiler simply generates optimized code for each concrete type.

Rust’s embedded ecosystem is also maturing. The community-driven rp-hal project has made Raspberry Pi’s microcontrollers some of the best-supported Rust targets today. Espressif officially supports esp-rs, and STMicroelectronics has begun releasing sensor drivers written in Rust. Even the Zephyr Project (a widely used real-time operating system originally written in C) is adding first-class Rust support for user applications.

That said, Rust is still a young language in embedded contexts. Compile times can be long, especially for large projects with many generic types. The strict type system, while valuable, can feel like overkill when you just want to toggle a pin. And because the ecosystem is still heavily community-driven, libraries and frameworks can change rapidly, often introducing breaking changes.

Another challenge is binary size. Although optimized Rust code runs quickly, static linking and monomorphization can lead to larger executables. That’s usually fine on modern microcontrollers with ample flash, but it’s something to watch if you’re targeting smaller devices.

Then there’s the learning curve. Understanding ownership, lifetimes, and borrowing is conceptually harder than grasping pointers and references in C. Many developers describe a “fight with the borrow checker” phase that lasts weeks or months. But the reward is confidence: once the code compiles, it’s likely correct in ways that C can’t guarantee at compile time.

Embedded Rust also suffers from another issue: writing low-level patterns, such as manual register manipulation and double buffering, require the use of unsafe blocks, which seems to defeat the purpose of using Rust in the first place! It makes me wonder if it’s worth the trouble over following a stricter set of rules for C or C++ (e.g. MISRA).

Despite these hurdles, Rust has been gaining a lot of popularity in the last few years. Major tech companies now use it for cloud infrastructure, operating systems, and security-critical components. In embedded systems, adoption will likely follow the same pattern we saw with Python and C++: slow at first, then accelerating as tools, examples, and vendor support mature.

For embedded engineers, the takeaway is simple: Rust is worth following and trying, even if you don’t have a need for it yet. With recent pushes for memory safety in low-level programs, you may find your company forcing it on you whether you like it or not.

Try It Yourself

The Intro to Embedded Rust Workshop at Supercon wasn’t about selling a language, as I wanted to focus on just exploration. Some attendees left convinced that Rust is the future of embedded programming. Others hated it. But everyone appreciated the time they spent with it.

If you’d like to try the same exercises, the entire workshop (complete with Docker image, source code, and step-by-step guide) is available in my GitHub repository (the README is the tutorial). All you need is a few basic components (see the README for the required hardware list), and a couple of hours. You’ll end up with a blinking LED, a working I2C temperature sensor demo, and a better sense of whether Rust fits into your own embedded workflow.

Whether you come away loving or hating it, you’ll have learned something valuable: a new way of thinking about safety, memory, and control in embedded systems.

Leave a Reply

Your email address will not be published. Required fields are marked *