Two things have kept me away from embedded Rust before
1. A huge chain of dependencies. Security issues aside, I prefer my embedded code to be lean and clean.
2. A big departure from how things used to be done in this world. For example interrupt handlers almost look like AWS lambda code to me. Maybe that's the future, I don't know. But right now I am not comfortable with this way of doing low level coding.
But I am keeping an open mind and will probably try it again soon.
I can see myself using Rust instead of C in more projects, but maybe first after things calm down a bit. I want my development environment outdated and boring, a.k.a. "stable".
I think there is another side to this coin though. IMO there are too many embedded shops that overlook reliable 3rd-party code in favour of spending man-hours growing their own code from seed instead. Or if not that you spend days debugging some greybeard coworker's "Oh I think I wrote something like that 25 years ago for a PDP-11, I'll just email it to you.."
But to be honest I don't really disagree all that much -- As much as the embedded world needs to keep up with the times, there's no way that the level of audit required for a reliable embedded project just can keep up with how quickly crates are being changed.
> 2. A big departure from how things used to be done in this world.
Sometimes I cringe too when takes a massive server board with 9999GB of RAM and a 4g modem and calls it "embedded" just because they screwed it to the side of a helicopter... But if the root question is "will rust be viable on a puny AVR" I hope that the answer is definitely yes.
There's still plenty of lingering issues (to name just the most glaring, you can't use a compiler since nightly-2021-01-07), but you certainly can use Rust for AVR. I've been writing all my ATtiny projects in Rust for the past year. I've been very satisfied with the ability to write high-level code (zero-sized types are a wonder!) that compiles to assembly more minimal than I'd write by hand.
Do you have any tutorials for this? I'm an embedded newbie and all the things I've seen required more knowledge than I have to either get to hello world or move an iota past it.
For AVR specifically, no. The Embedded Book [0] (which uses the ARM-based Discovery board) is a great introduction.
I can envision a future where clean abstractions are written that can provide an Arduino-like experience in Rust, but we are still a long way from there.
You are making me want to write some Rust AVR tutorials. Once we get the latest compiler building AVR code again, that will be my next focus.
>Sometimes I cringe too when takes a massive server board with 9999GB of RAM and a 4g modem and calls it "embedded" just because they screwed it to the side of a helicopter...
Why should the processing power matter for whether something is embedded or not? To me that just seems like cost optimization for an embedded project.
Yes I agree such a system is definitely embedded.. I think I was deliberately constructing a straw man in a (bad) effort to poke fun at people such as myself who have 'opinions' on what embedded 'really is'... "640k is enough for anyone" "C compilers are for people who cant write good assembly" etc etc.. https://xkcd.com/378/
I think it's possible to accidentally use that straw man to justify fears when looking at embedded rust.. But as other people have pointed out in this thread, just because rusty interrupt handlers look weird doesn't work any better or worse than one written in C.
> A huge chain of dependencies. Security issues aside, I prefer my embedded code to be lean and clean.
Where does your limit go for that? I wrote a eurorack modular embedded thing on Teensy 4.0 (imxrt) in Rust this year. I just inspected the dep tree, it's 38 crates, with 1 being my own.
Cargo has its own interpretation of semver: first non-zero number is the major version, so v0.6 is like v6.0.0 and v0.7 is like v7.0.0. Then each major version is treated as a separate crate, as if it was cortex-m-v6 and cortex-m-v7.
I can definitely see the objection to running Nightly to build actual embedded systems. Two ex-colleagues built embedded devices that were intentionally injected into glaciers for measurement purposes, obviously "Oops, we need to upgrade them" isn't much more of a thing while they're working than for the Mars rovers - if you don't have a way to remotely upgrade the part that needs changing, or your radio upgrade fails and you brick the device you can't send a field technician to fix it.
And personally I never run Nightly, I did AoC for example entirely in stable because I don't feel like I need more excitement in my life.
The "it looks alien" thing (which is how I read your comment about "looks like AWS lambda code to me") I think is a place where Godbolt can help you if you're serious enough about low-level to be at least confident reading assembler even if you never write any. Matt Godbolt built Compiler Explorer's ancestors because he knew that nicer C++ iterators claimed to have the same performance characteristics as the good-old C style for loops he'd grown up with but he needed to be sure that was a fact before he moved vital performance critical software to them. Compiler Explorer shows that yup, you get equivalent assembler, the machine is doing the exact same work as before. I think you'll find Rust's "like AWS lambda code" for interrupt handlers is eventually doing the same low-level bit twiddling you're used to writing, but presented in a different (safer) way.
FWIW HN formats the space-prefixed paragraphs in your text as fixed pitch like code, which is annoying for people on small devices when you aren't actually showing code or diagrams where spacing is important to understanding.
Using Rust mods is exactly being lean and clean: would you rather take a bunch of include files, stupidly duplicates them in almost every translation units, and this would eventually blows up at some point of time, and is hard to reconfigure with some mysterious macro induced error, or, would you inter-depend on bunch of crates that might pull over 100mb of stuff at total that might have potential supply chain attack because it is too convenient to use modularized code?
I myself rather take the latter, at least the risk of not compiling successfully is lower and the chance of getting something done is higher. Security issue aside, there are crate scanners that exactly prevents this, and how can you do that with C?
Also, embedded code looking like Lambda code is a very good thing: both embedded system and Lambda are focused on doing one thing and do its best
Explain the crate scanner thing? Ignoring security issues due to crates is no better than ignoring security issues due to memory errors or undefined behavior. And if you are doing safety critical code, where rust would shine, all those dependencies need to be certified to the same level as your own code. Sometimes re-creating and testing and certifying exactly the code you need is faster than reuse. Heresy I know, but it has been my experience with both embedded c++, and rust at work.
Edit: We have not deployed rust to safety critical yet, I am unaware of any certification that would allow that existing for any version of the rust compiler.
"[cargo-audit] Audit Cargo.lock files for crates with security vulnerabilities reported to the RustSec Advisory Database."
"cargo-deny is a cargo plugin that lets you lint your project's dependency graph to ensure all your dependencies conform to your expectations and requirements." e.g. license, security advisories, source.
I really hope embedded rust can become the next rock-solid foundation. I need my code to build/run today and also in 10 years. We have code in our codebase from at least the early 90s but I suspect it's older than that.
I went into embedded because I really despise the code churn of higher level frameworks/languages. "We just released Qt5! Good luck rewriting your code."
If that's your goal I think you should wait a few years before starting using Rust. We have already seen early Rust code not compiling with new compiler releases. Right now we are in a move fast and break things phase.
Also, you need to be careful about what you implement yourself and what you import from crates. Otherwise it will be qt5 all over again.
> We have already seen early Rust code not compiling with new compiler releases.
I haven't really seen that happen with post-1.0 Rust, which is everything onwards of early 2015. I believe they've since tightened a couple of edge cases due to soundness issues, but nothing worse than that.
Would be interesting to hear about your contrary experiences.
No, it's on stable rust, maybe you got confused by the bug report talking about the nixos-unstable channel, that has nothing to do with the rust nightly builds, it's the version of nixos that is currently under development.
This is true for aarch64, but if you're not doing an embedded-linux style project, it's very likely you'll need nightly for inline assembly, though a significant chunk of that is being stabilized as we speak (though sadly not yet enough for my work...)
Good point. Though you can always link to .S for that last bit, right? I don't mean to tell you your business, I know you folks are doing much more intense stuff than I am. It doesn't surprise me that you'd know exactly what's keeping you from using stable.
You may have started getting deprecation warnings because some methods in the `Error` trait have been deprecated [1] in favor of a different mechanism.
Then, you may have configured warnings to result in compilation errors in your build/project, however, I would argue this situation is not what most people would understand as "code not compiling due to a compiler update".
Deprecation should be the only change to Error in the last six years. It hasn’t moved. If you ever get a repro would love to hear about it! This kind of thing doesn’t happen at the scale we’re at anymore, if it did there would be an uproar.
> We have already seen early Rust code not compiling with new compiler releases.
Are you talking about breaking changes in the compiler after Rust 1.0? That should be very rare, and generally easy to fix (e.g. by adding a few type annotations).
Or did you use unstable features? (Not sure if embedded is usable without unstable nowadays)
One of the things I really find limiting in Rust compared to C/C++ is conditional compilation, i.e. for different architectures: yes, the C/C++ pre-processor does suck in many ways and is masochistic if you're not careful, but it's also incredibly flexible.
Conditional compilation in Rust can be done per module, so in theory you can have different archs in different .rs files, each imported conditionally, but that then seems to mean duplication of things like structs, method/function signatures in different implementations, which is pretty annoying in many cases (i.e. you'd just want the inner bits of functions to be different, or optionally call certain subsets for certain archs). You can abstract some of this to 'common' modules to a limited degree, but not much in my experience, and that comes with additional 'plumbing' complexity anyway.
Rust has cfg attributes, and the cfg_if crate which in theory is a bit closer to the pre-processor functionality (and to a degree allows nesting/cascading like in C/C++), but in my experience these are just as annoying and limiting but in different ways: it's a macro, which is annoying in other ways (needs braces, has some issues with formatting, etc).
cfgs also seem to be exclusive, so I've found it incredibly messy getting a balance right between code re-use for common parts (i.e. function/method signatures) which need to be shared by multiple cfgs, and duplication.
I just checked.
You can also add those same conditionals to functions and structs.
So you don't have to in- or exclude whole modules.
You can even use it in an if-statement, as seen in the example main function on the page I linked.
It's do-able with cfg_if crate macro, but I really don't think much of the result in many complex situations, compared to what can be done in C/C++.
Also, using the cfg! macro (which you need to do to use it in logic) I think is stripped out at link time (I had all sorts of issues with this), so if you've got intrinsics which don't compile on the current platform, that's not helpful (maybe I did something wrong here, but I've googled it a lot, and asked for help several times on the Rust Discord server).
It's do-able, but you seem to have to go out of your way organise code or modules in sub-optimal ways to compensate in many cases.
Most of the crates which I've seen which do that kind of thing in my experience seem to use features or conditional modules, which as I've discussed above have other downsides. Others like Vek seem to just get LLVM to do the work.
I think that's a bit disingenuous: I've certainly found Rust's infrastructure in this area quite limiting and a pain point for myself.
There are many examples out there of ways to deal with conditional compilation in Rust, and I find it much more straightforward and simple than my own personal experience with C.
Take a look at the socket2 crate as a nice way to deal with different platforms.
The rule of thumb I would use is encode logic as much as possible into traits, and then abstract out specific arch details like fields etc. You can combine conditional compilation units with trait based support as well to reduce the annotations and instead provide functionality only for types that implement certain traits.
Are you able to provide a barebones specific code example (either in C/C++ or Rust) of the issue(s) you're describing (e.g. nesting/chaining) where Rust conditional compilation is limiting?
A specific example is likely to lead to a more educational discussion in terms of learning about a solution to issues that you've observed or highlighting currently unsolved issues with Rust's implementation.
> yes, the C/C++ pre-processor does suck in many ways and is masochistic if you're not careful, but it's also incredibly flexible.
Why not just use it as-is for Rust ? There are plenty of non-C/C++ things which use cpp. X11 uses it for config files (.Xresources for instance afaik). I think that polkit or something like that uses it too for its config files. I've seen it used in Java.
I would like to see that avr-unknown-gnu-atmega328 works with the latest compiler. Some bug in LLVM broke it[1], because it didn't work starting with versions after nightly-2021-01-07[2]. I know that rust team has nothing to do with this, but they should improve the gcc codegen[3] to be able to run rust on more embedded devices than LLVM has support for. Someone wanted to port his libc written in rust to ia64 and the gcc codegen broke and couldn't compile that.
Do you have experience with AVR assembly and calling conventions? There are only a handful of us looking at this issue, none of us have much bandwidth and we could definitely use your help!
The AVR codegen bug is discussed here: https://github.com/rust-lang/rust/issues/82242#issuecomment-... Seems to be due to the LLVM-AVR patchset somehow expecting gcc-compatible compiler intrinsics (for division), whereas Rust provides intrinsics derived from LLVM's compiler-rt, with different calling conventions.
Espressif (the makers of the hugely popular ESP32/ESP8266/... microcontrollers) hired an embedded Rust community member to work full time on Rust support for their chips:
https://mabez.dev/blog/posts/esp-rust-espressif/
There's at least a decent amount of interest from both industry (auto tier 1's, for example) and from vendors; I'd love to name names here but I'm pretty sure I can't. :)
> But are there any vendors actively participating in or supporting Embedded Rust?
That's kind of an interesting question.
While obviously vendor participation can lead to some positive outcomes; my impression is some part of the motivation for Rust targeting the embedded space (and not waiting on vendors) is to avoid reliance on hardware vendors.
Due largely to the poor reputation hardware vendors have in regard to anything in the software tool/firmware space within the C ecosystem.
In addition to what others have said, ARM has participated in the RFC process, donated CI resources, and joined the Rust Foundation.
There are also a large number of things going on in industry that aren’t yet truly public. Some of these are the “I know something I can’t say” variety and some are the variety of “wow another car manufacturer has posted an embedded Rust job?”
I haven't checked Embedded Rust yet, but it is in my todo list. However, my two main embedded targets for C are Z80 and 8051, and my understanding is that these targets are not supported. I'd love to have Rust there.
I can see myself using Rust instead of C in more projects, but maybe first after things calm down a bit. I want my development environment outdated and boring, a.k.a. "stable".