Programming languages tend to polarize, and Rust is by far no exception. Whether it will stick around and grow as an alternative for the lower levels or not — time will tell. In the meantime, if you’re curious about the language and its low-level abilities yourself, [phil-opp] has written a series of blog posts on building your own little bare metal kernel in Rust.
Starting from the basics, [phil-opp] describes in detail the set-up and build process to create a standalone executable that won’t be linked against the Rust standard library. From here he proceeds to build a simple operating system kernel that prints a good old Hello World via VGA output — QEMU emulation included. And of course, there is a GitHub repository with all of the source code.
[phil-opp] has been working on this for a while already, and he is currently writing the second edition of the series. Some content is therefore still missing, but you may find more of it in his first edition. And in case you know absolutely nothing about Rust in the first place, let’s just take a step back and start with the basics. After all, we might see more of it in the future.
36 thoughts on “Pun Intended: Bare Metal Attracts Rust”
I don’t know.
I don’t care.
Can you repeat the question?
“Programming languages tend to polarize, and Rust is by far no exception.”
Call me interested when you don’t need a 64 bit Intel monster to run your code on. Say something more along the lines of a 80×51, Pic32 or STM32 $3.00 board off Ebay.
Ah, I should have added one more link then: https://hackaday.com/2017/05/03/rusty-arm/
Uses the STM32 Discovery board – not quite $3.00, but it should theoretically work with any Cortex-M, including the Blue Pill.
It’s also worth adding that the effort by japaric documented in the old hackaday post grew and grew (he did an enormous amount of work and shipped a ton of code and tooling and documentation) and spawned the rust-embedded working group. When the rust community decided on their roadmap for 2018, embedded devices were chosen as one of the four target domains to focus on for the year with the goal of embedded devices being a first-class target by the end of the year. The embedded support in Rust has been moving along like crazy this past year and it really has been getting a ton of support from the Rust community as a whole.
Looks promising, but far from being ready for prime time at this point in time. IOW you aren’t going to be winning over folks who are used to a polished development environment.
Looking at the effort required, it’s clear you really have to be a Rust guru to pull it off on the STM32.
No thanks when there are much better options where I don’t have to spend the majority of my time tweaking the tool set and writing scripts.
> No thanks when there are much better options where I don’t have to spend the majority of my time tweaking the tool set and writing scripts.
You have that the wrong way around: With Rust you can focus on code instead of focusing on the build environment to work. It’s incredibly easy to get from 0 (no Rust compiler installed on the machine) to blinky (on an already supported chip) with just a hand full of commands. While the ecosystem in some respects is not quite as advanced as others (like HALs only existing for a few dozens MCUs whereas others supports hundreds), things like the embedded-hal traits make writing (fully portable) drivers easy and fun so there’re lots of them (and I’d wager even more than for some of the more popular environments with the big exception of Arduino).
Feel free to dig into the resources list here to see what’s what: https://github.com/rust-embedded/awesome-embedded-rust
Sure, not a problem… For MCUs you might want to checkout https://github.com/thejpster/monotron for a self-contained system or https://www.tockos.org/ for an OS. Of course if the blue-pill is your thing, that is nicely supported by Rust, too with a HAL crate here https://github.com/japaric/stm32f103xx-hal (used to be known as blue-pill). Oh, and if you’d just like to browse around embedded Rust stuff: https://github.com/rust-embedded/awesome-embedded-rust
Embedded Rust has been going through massive development this year, and can be used on Cortex-M, MSP430 and RISC-V. Is that interesting enough for you?
For those of us whom have yet to install a Rust compiler, an actual pre-built binary would have been nice to see. The nice thing about emulators, you don’t have to be on the author’s default platform to see the outcome. It is very nice to see instructions on how to build it, but then also have a pre-built binary as well.
People often don’t post binaries because building a binary from a Rust project is as easy as `cargo build –release` / `cargo install`.
However, being that I have never used Rust, nor have I owned or used a Linux machine, `cargo build –release` / `cargo install` means nothing to me. I haven’t a clue what you are talking about. I would have to search the meaning of that. My point is, a project designed and intended for running under an emulator, means that the platform in which it was created is completely independent to the end means, the binary. The binary should be able to be emulated by any compatible emulator on any platform, whether the end user has access to the initial platform to build it on or not. Since I do not have means to a Linux machine, a Rust compiler, nor the knowledge of `cargo build –release` / `cargo install`, a project such as this should include the binary so that one like me could emulate it on QEMU built for a completely different platform to see the end result. If the end result was intended for a specific platform, such as Linux, this would be a different story. Anyway, thanks for your comment.
So ….. apart from being new and different, what does Rust offer over bare metal programming in C and assembly?
I mean new and different can be good for variety, but sometimes you just want to get things done.
The advantage is, basically, that it allows you to use dynamic memory allocation with smaller risk of having memory leaks and/or dangling pointers, but without the memory and CPU footprint of a garbage collector or another system for memory management (like reference counters). The analysis is done during compilation, so there is no extra checks in the code that eats memory or CPU time.
The inconvenient is that you have to learn a new language, and also you need a compiler for your processor. That means that probably we will never see a RUST compiler for the PIC16 or PIC24, or ATMega processors.
In fact, that’s why I tried to have the best of both worlds and created an static analyser for C that gives me precisely those advantages, but since the code it uses is still pure C, it doesn’t have the inconveniences, so I can use it with any C compiler, IDE and toolchain. It helped me to find several memory leaks in my PIC24 code in a big project.
Rust for AVR (e.g. ATMega) is in the works: https://github.com/avr-rust/rust. There’re still a few kinks to work out with LLVM but there’s definitely hope.
Sure, but you still have to learn a new language, and also you still depend on an unofficial tool to compile your code. At this moment I prefer my solution, at least until Rust is officially backed by the manufacturers.
Yeah, but, you’ll also soon die. Those up and coming won’t have to learn a new language, they’ll just have to learn a language. This is how progress happens. Us oldies die.
Frankly, my dear, I don’t give a damn :-P
This is called progress. Accept it or stay in the past.
That’s a bit of an understatement because what you describe is a subset of what Rust enables.
More generally, Rust allows you to encode a lot more invariants in the type system and have them verified at compile time.
For example, because of Rust’s ability to prevent use of stale references, it’s possible to compile-time verify correct use of any interface that can be modelled as a state machine. The Hyper library for Rust uses this to catch the classic PHP “Can’t set headers. Body already streaming.” bug at compile time. (The call to start streaming the request body takes ownership of the old reference and returns a new one with no “set header” method.)
In the embedded sphere, there’s a lot of work being done to verify the sanity of the GPIO pin configuration and usage at compile time.
Rust was a respectable attempt to make a C/C++ dialect less dangerous for developers.
However, as it sounds like it complicated some desirable low-level aspects of C/C++ use-cases… it annoyed many people.
I have yet to personally see a Rust use-case where the languages weren’t functionally equivalent, but it is how it is perceived.
For high-level abstract tasks I have been studying julia lately, as it meets several of my current project requirements:
Also, julia doesn’t claim it is a safer replacement for C/C++ based on one groups opinion, so I tend to be less skeptical of the designers motives/sanity. The open julia language does offer quite a few actual improvements as a high-performance llvm compiler, but it will take time to see if it lives past v2.0. ;-)
> However, as it sounds like it complicated some desirable low-level aspects of C/C++ use-cases… it annoyed many people.
Has it really? I can’t think of any scenario that C or C++ can do that’s been complicated by Rust. Rust requires that you opt into unsafety with the `unsafe` keyword, and has methods which are explicit in their intent, but other than that it’s the same. You can continue to avoid best practices and pass around raw pointers as you do in C and C++.
Personally, I think it is probably the lack of board-support-package styled Rust API stub compiler xml templates for 99% of the OEM chips around, and the required amount of effort to implement bug free abstractions. Digging through 100 thousand lines in multiple layers of abstracted auto-generated meta-circular code is probably less intuitive than an app note memory layout for some people. However, if you don’t trust your own team’s use of pointers, than C/C++ and Rust itself is probably not a good choice for the project anyway.
Unless your app is timing-critical, or your platform has only 4 bits, you souldn’t use assembly at all. With assembly sooner or later any programmer develops a library of standard code snippets. C replaces them with single keywords or predefined functions that were created by better programmers. C is not the best solution, but it has tradition, standard libraries and lots and lots of users, so every possible problem was solved by someone already. I hate it but it’s still the best tool for embedded platforms…
Rust has a nice logo, and that’s all that it offers. It is a better C implementation” minus all the experience and expertise available. And does it run on 8-bit micros? If not, then I’m not interested…
The lack of 8 bit support is a show stopper for me. I guess Rust was never designed to work with mcu’s with small memory footprints and limited processor power.
> The lack of 8 bit support is a show stopper for me.
Given that you can target a MOS6502 using Rust (Rust can target anything that LLVM can target), I wonder which 8-bit platform you are interested in targeting and how hard would it be for LLVM to support it.
Basically the point of this video: https://youtu.be/t99L3JHhLc0
Compared to C, you are able to achieve a desired result with less code, which is much less prone to fault due to static code analysis and the type system. Without the standard library, you still have access to algebraic data types, pattern matching, generics, and the borrowing and ownership system, as well as access to a complete ecosystem of libraries on Crates.io that you could import into your project. Rust can basically guarantee that all the code you write will adhere to best practices in C.
Embedded is why I’m investing significant personal time in learning Rust. I saw a video running Rust on an STM32F411RE and learned that there is a legitimate alternative to bare metal C/C++. (D was interesting, but can’t run on embedded. Not sure if Go can. Not sure if micropython has the speed.)
Anyway, I can’t wait till Rust has full support for embedded development. All I really need are basic hardware & OS abstractions, which will allow me to spend more time on my application than managing header files, inconsistent HAL/CMSIS libraries, memory, etc. Granted, embedded devices still require micromanaging a lot of resources, but with Rust, I won’t have to micromanage the language itself.
The Rust Programming Language book by No-Starch is a pleasant read, BTW. After reading chapter 4, the language got MUCH clearer and I’m now developing small, but productive/useful programs.
If you decide to try Rust, get an IDE with Rust plugins. The hover information is critical to learning Rust. I’m using Atom and the plugins tell me what type each variable is, and clearly shows my mistakes when trying to use the wrong type. Often types are wrapped in other types, like Option or Result, and this is not obvious from the code itself.
On the down side, I’ve been trying to interact with a modem over a serial port, and finding the right mix of libraries, variables and examples has proven frustrating. After a day of fighting with it, I finally got it working smoothly, AND I understand WHY it works. Learned a lot, and quite satisfying.
Rust on embedded is exactly what I’m looking for…
I’d rather use Nim.
Please be kind and respectful to help make the comments section excellent. (Comment Policy)