# Embedded: C is soft and Rust is firm By now most of you heard of [Rust](https://www.rust-lang.org/) and [why you should consider it](https://medium.com/better-programming/why-you-should-consider-rust-for-the-next-language-you-learn-39652d8e1bbe). Since it's release 5 years ago Rust has made steady progress to the point it is now fully supported in the [Windows SDK](https://github.com/microsoft/windows-rs), and Google is starting to fund [rewriting popular open source in Rust](https://security.googleblog.com/2021/02/mitigating-memory-safety-issues-in-open.html). Given the C/C++ ecosystem has been unchallenged for almost 50 years, the unthinkable is now happening. But it should not come as a surprise, as computer sciences have advanced quite a bit, and developer computers even more. Most of these innovations have not made it into C/C++ as this would require abandoning the current eco-system. Rust is the chance to do so. All of these advances sum themselves to Rust's own banner innovation, which is memory management at compile-time. So Rust works like C, but there is no need to explicitly allocate and deallocate memory. The compiler does so with such perfection that ends up revealing most bugs. The result is a productivity language with mathematical underpinnings that make it both more reliable and more performant than previous languages. Rust is now the preferred tool for software engineering professials. For the first time they have a tool that meets their need for precision. Precision allows them to have higher quality code, be more productive, and to easily take advantage of shared code which inherits those traits. As Rust has no real competition, this is snowballing and the industry is now converging on it. ![](https://i.imgur.com/MnWDjES.png) Though Rust is unique, Rust is part of a broader movement towards productivity, which is happening throughout the software world. For example: - [Kotlin](https://kotlinlang.org/) is taking over Android development, - Apple is replacing Objective-C with [Swift](https://developer.apple.com/swift/), - [Flutter](https://flutter.dev/) is reimagining both graphical interfaces and cross-platform development, - [Julia](https://julialang.org/) is a [unreasonably effective](https://arstechnica.com/science/2020/10/the-unreasonable-effectiveness-of-the-julia-programming-language/) tool for science and - [Go](https://golang.org/) is a lesson in simplifying a language. In comparison with these languages Rust is in a class of its own, as the only C/C++ replacement, and capable of ranging from the lowest to the highest levels. Rust is a far bigger language than C though, with a lot more details thrown in one's face, that in C mostly fly below the radar. Think of the size of integers, the presence of several string types and the universal presence of generics. Though familiar to C++ programmers, it takes a bit of time for the eyes to see through the syntax. In Embedded though typically the core syntax is used, making it easier to get started. The language itself nevertheless feels much like C but it has the power and ergonomics of a modern language. C++ concepts like Objects and Exceptions have been removed, making it more like C, and more predictable. On the desktop Rust typically performs as fast as C, in Embedded it will be a tad slower due to the fact that Rust performs bound checks (by default). ## What about C/C++ and Embedded? In Embedded C/C++ reign and a diverse patchwork of tools, IDE's and compilers target different processor families. We are accustomed to having many different development environments, crappy debugging, and tools that haven't been updated for a long time or only work on Windows, barely. Heck, many write code in C89 style to this date, just because the libraries of the tool-vendors do so. When C++ is used, a C compatible subset is typically used, as C++ is by far the largest language in existence, with many intricate details, impossible to fully know. Most collegues don't even [know C fully](https://kukuruku.co/post/i-do-not-know-c/). Footguns never cease to appear in new and miraculous ways and require hours of debugging. Even downloaded code almost never compiles without at least some tweaking or tooling. This crappy state of affairs is what shaped us. Elsewhere many have moved on to dynamic languages, but those in Embedded have weathered this most hellish environment and some have even thrived despite all of this. Every week we struggle with problems that are often only distantly related to the actual job at hand. The mere thought of an entirely new ecosystem wears us out. Instinctively we think we won't survive a second onslaught, because we barely survived the current, and so we stick to a preferred set of tools and hone our skills synergistically. The eco-system has so much shaped the thoughts of so many of us in this way, that it is hard to conceive that things have changed, now that they finally have. Though Rust can move us away from the state of despair that engulfs us, this change is nevertheless not easy to assimilate. ![](https://i.imgur.com/09Lh2Cz.png) Take the blogpost ["Rust is not a good C replacement"](https://drewdevault.com/2019/03/25/Rust-is-not-a-good-C-replacement.html) from 2 years ago, that exemplifies the response this tech anxiety provokes. It asserts: - *"C is the most portable programming language"*. This is true and likely to remain true. Rust uses [LLVM](https://llvm.org/) as its back-end and that has limited support for 8-bit MCU's. Only the AVR is partially supported. For Rust you'll need an ARM or RISC-V processor. - *"C has a spec"* and *"C has many implementations. By having many implementations, we force C to be well defined, and this is good for the language and its long-term stability"*. We all know this to be a lie in embedded: there are many slightly different compilers, each with their own bugs and limitations. Editors are crappy. Modern versions of C are hard to come by. So we mostly default to C99 and MISRA exists to work around the language. Rust in contrast supports all targets with a single compiler, a single build system with a choice of modern editors. The latest compiler can be used without issue and noone needs to be educated about historical artifacts. - *"C has a consistent & stable ABI"*. Though the Rust compiler can reorder fields in structs as needed, that doesn't play well with the outside world and legacy code. So [C-structs can be declared](https://rust-embedded.github.io/book/interoperability/c-with-rust.html) and work as expected. They just aren't the default, as this would prevent optimizations. - *"Cargo is mandatory"* but *"compiler flags are not stable"* and *"Rust refuses to play along"*. So things would be well if Rust played along? But if Rust did that, it would perpetuate the fragility and pain that is especially abundant in embedded. Rust is moving away from this. [Cargo](https://doc.rust-lang.org/cargo/guide/why-cargo-exists.html#enter-cargo) standardizes the way external code is pulled in and *reproducibly* automates the build process. Compiler flags also no longer live in obscurity and their use is easily commented. Both Cargo and Rust reduce the need to know compiler flags. - *"Concurrency is generally a bad thing"* and Rust's *"“Fearless concurrency” allows you to fearlessly employ bad software design 9 times out of 10"*. Though concurrency in Rust is safe it does come with the price of added complexity. As the author states, that can be avoided by reverting to a simple event loop. It seems the author just doesn't fathom concurrency in Rust is safe. And better. When you need it. Then Rust has your back. No such option in C. - *"Safety. Yes, Rust is more safe. I don’t really care. In light of all of these problems, I’ll take my segfaults and buffer overflows"*. And add races and [footguns](https://news.ycombinator.com/item?id=17393292). Yes, C is easy to get started and easy to get initial results. But the quality needed in software engineering is much harder to achieve and sustain. The experienced developer has to be on constant alert, in this order: 1) avoid the pitfalls of the language and the ecosystem, 2) think about the application, and 3) think about architecture. That's a high load because problems inevitably get in the way at the worst time. Problems that were appropriate and natural for a time where bytes and processor cycles were extremely expensive, and the developer machine was basic at best. Those days have long gone by. Rust reverses this order, pushing one to start with the architecture. Once that's right the program almost writes itself, comes out without hidden defects and often works as intended... that's a big but valuable change. The author however looks from the outside in, and misses out. Most of the arguments made echo despair, and that in itself argues against the case he is making. ## Bugs have organizational costs In the non-embedded world, organizations have invested heavily in devops, test engineers and continuous integration. Especially when they use dynamic languages. Their existence proves that bugs are extremely expensive. When there is a high financial risk associated with bugs, that also leads to code bases that are not refactored when they should be. Rust eliminates most of those risks. In Embedded the ability to test is limited. Unit testing can typically be done well on libraries, but applications are mostly human tested. Network protocols need the most testing, yet real-time behavior is especially hard to capture. So in embedded a language that outright eliminates whole classes of bugs, like Rust, can make a big difference. Google and Microsoft report that 70% of safety issues are due to bugs Rust fully prevents. Those are the hardest kinds of bug to debug, due to memory leaks, buffer overflows and memory corruption. It is therefore reasonable to conclude that Rust eliminates more than 70% of the debugging needed in embedded, and this alone would make and engineer 50% more productive. Though less debugging is a great time saver, Rust also is a language that allows higher level abstractions to be used (without any run-time costs), allowing more reuse, while its safety makes it low risk to take advantage of shared public code. So expect engineer productivity to double over time. Regardless, the real cost savings are downstream, in the organization. Organizations employ teams and devops to get rid of bugs before they reach the market. Bugs that nevertheless get into the market can be very expensive to correct, or give the product a bad name. Embedded products with bugs can be outright worthless or dangerous. Some bugs may even cause the organization to fail. ![](https://i.imgur.com/uTsHsVN.png) Besides the absence of bugs, Rust has additional features that help to quickly create reliable code: - device peripherals can be setup correctly, without error, [using only the type-system](https://tweedegolf.nl/blog/39/why-rust-is-a-great-fit-for-embedded-software) - Rust has built-in support for async primitives, much simplifying asynchronous code, a [luxury on Embedded](https://tweedegolf.nl/blog/50/build-your-own-async-primitive) even C++ didn't offer - Rust is fully compatible with C-libraries and can compile C-code in its build system using build scripts written in Rust itself (example: see `main()` in [build.rs](https://github.com/wyager/vumeter/blob/master/code/audio_board/build.rs)). - [Unit-tests](https://doc.rust-lang.org/rust-by-example/testing/unit_testing.html) are integrated. So Rust has unique advantages in embedded. Now with all this said, Rust is not all [roses](https://tweedegolf.nl/blog/42/potential-improvements-for-rust-embedded-abstractions) and [perfume](https://www.youtube.com/watch?v=PYJ6y1kG4oI). Learning Rust implies assimilating a wealth of new information. In Embedded this is made worse because of the build system needed to cross compile to and debug on a remote device. Although much of the knowledge from C/C++ transfers, it is still like learning to speak a new language: the same words no longer mean exactly the same thing. The best way is to dive right in, with a concrete problem to solve, and practice. Some are proficient within a few weeks. Commonly cited complaints against Rust, a initially overwhelming syntax and potentially long compile times are less of an issue in Embedded, as Embedded projects are smaller and easier to get started with. ## Wrapping up You may have noticed I didn't go out of my way to appease C/C++ diehards, by laying out how entrenched, extensive and difficult to replace C/C++ is. That's the kind of post often seen in the early years. I think we are beyond the point where Rust has to justify itself and pay hommage. The way I think of Rust is that Rusts advantages are undeniable from an organizational perspective, and that management will soon take note. Also, no amount of hommage can cover up the fact that C/C++ is a pig to work with. I feel it makes us worse engineers, not better ones, so it would be immoral to present C/C++ as anything other than a pig. We basically hack our way around it, and that's what we must be good at, at minimum. Instead of doing solid engineering. However, if you like C/C++, and know how to deal with it, by all means, do so. In the right hands it can certainly be wielded into a fierce weapon. But it would not surprise me if a few years down the road you feel left behind, as managers start pushing Rust, having seen what C++ can do to the organizations that use it. So that new Embedded project? You may want to [try your hand at Rust](https://circuit4us.medium.com/rust-on-stm32f103-blue-pill-with-probe-run-tooling-b596f0623091). References: - [Rust is not a good C replacement ](https://drewdevault.com/2019/03/25/Rust-is-not-a-good-C-replacement.html) as cited - [I do not know C](https://kukuruku.co/post/i-do-not-know-c/) and [So you think you know C?](https://hackernoon.com/so-you-think-you-know-c-8d4e2cd6f6a6) - [Why You Should Consider Rust for the Next Language You Learn](https://medium.com/better-programming/why-you-should-consider-rust-for-the-next-language-you-learn-39652d8e1bbe) - [Why Rust is a great fit for embedded software](https://tweedegolf.nl/blog/39/why-rust-is-a-great-fit-for-embedded-software) - [Rust Embedded Book](https://doc.rust-lang.org/stable/embedded-book/) - A highly recommended [example project](https://yager.io/vumeter/vu.html#software), including a (rare) [build script](https://github.com/wyager/vumeter/blob/master/code/audio_board/build.rs) that mixes C and Rust code. - [Ferrous Systems](https://github.com/ferrous-systems) has modern embedded related material and videos - Quick-start using the infamous [Blue Pill](https://circuit4us.medium.com/rust-on-stm32f103-blue-pill-with-probe-run-tooling-b596f0623091) - [Migrate C code to Rust](https://github.com/immunant/c2rust) - [Build your own async primitive](https://tweedegolf.nl/blog/50/build-your-own-async-primitive) (advanced topic)