Rust is a brilliant candidate for embedded systems - safety, built-in package management, it's ergonomic and pleasant to work with. But it has one fundamental problem, which the entire community refuses to address: the steep entry. Several months ago I was making a wedding gift for a friend which almost exclusively involved embedded development. And having several brand new STM32's laying around I initially opted for them and rust. In all fairness the entire thing did not involve all that much: If I recall correctly, an I2C display, RTC, sound sensor, some adafruit soundboard, sound amplifier+speaker and a couple of push buttons. I went against one of my own core philosophies of not using something for the sake of using it, that being rust on this occasion. In theory the STM32 should be a great candidate since the Embedded Rust Book revolves around it. Well... No. You see, each time you decide to get something rolling, you have to work your way through tons of configuration, completely blind. And just as you are convinced that you did everything right, compile your code and flash the device, you get a cryptic error and you are left clueless as to what it may be. It took me 7 days to get the I2C display going. I guess you could slash a few hours since I had to get Cyrillic letters out on the screen but that was just a few hours at best. At one point I simply gave up because I didn't have that much time to waste and ran back to the trusty Arduino. And this is fundamentally what's crippling the embedded Rust world - it's way too much of a hassle to be considered a good option when you have to deal with a schedule - you have to spend absurd amounts of time fiddling with setup and poor documentation. If those issues are addressed at one point, I'd gladly ditch arduino for good. But until then the embedded rust is just some fun toy to play around with on a day off.
> But it has one fundamental problem, which the entire community refuses to address: the steep entry.
Do you have any evidence for this? From what I’ve seen, the Rust community has invested considerably in creating learning resources and so forth to make getting started easier. However there’s only a finite amount of time/people working on it, so rough edges remain for less common use cases
Two major ones: first one is the obvious - I don't see a whole lot of effort to make the first steps easier. Second one is I brought this topic on several occasions in the rust community, the closes I got to a sensible(note the word closest) answer was "well you could package development environments for different hardware inside docker containers". Which for better or worse gives all the credibility one might want to this[1] comment.
Rust doesn't have a volatile memcpy yet, bitfield ergonomics are poor, global variables require thread safety even when targeting a single core device, the memory model isn't finished, and upholding the rules the compiler expects you to respect in unsafe code with regard to aliasing is difficult to wrap your head around (pointer provenance, even the author of tokio got it wrong).
I’m curious how well you knew Rust prior to starting? I’m learning Rust at a very slow pace. I finished a language specific course, going through advent, and I’m planning on going through an embedded Rust book next. I can imagine attempting to jump straight into embedded Rust could be very challenging.
I get the sense they weren't talking about what most people mean when they say "steep entry" in a Rust context (i.e. the language itself), but specifically its support and documentation for the embedded case
I jumped into embedded C and had working firmware in a couple of days. Of course I knew C but I am no expert and there was like 10 years since I've used it last time and that last time was not a firmware.
Rust has been my goto language for several years for multiple projects, was a primary language at a certain point not too long ago for what it's worth. Generally depends on your background. Rust in general suffers from one... I guess you can call it disease: evangelists. Many people are convinced that it is the best language for any purpose and you'll never need anything else(something which can be seen in the js and java community and we know the outcome of that). To quote a guy I used to work with: "The perfect web stack is actix + rust wasm. You can't possibly need anything else". Uuugh... Yeah... Sorry, no.
It's definitely a language you can't learn overnight so don't overdo it - it takes time to wrap your head around many of the concepts. While I admit I am a fanboy, I also openly admit that it's not suitable for every purpose. Most certainly not for web development - yes it is fast, yes actix can boost some astonishing benchmarks but truth is it hardly pays off to put in so much effort, especially when you are working on a schedule. It's a question of trade-offs. People address many async-await logical blocks by slapping Arc<RwLock<T>> or Arc<Mutex<T>> everywhere and a ton of read and write locks to get around it. Which if perfectly acceptable if you don't overdo it. Otherwise you will have to spend endless hours trying to catch some lock somewhere which is biting your head. Hence the reason why I'd really avoid it for large and complex web services for instance. There are other areas where it's perfect. Three personal projects I'm working on(which I'll hopefully get to open source over the next 6-8 months) and where I don't regret picking rust:
* Raspberry-pi powered CNC router(though dealing with gcode is a huge pain in the ass).
* Distributed full-text search engine(similar to elasticsearch).
* Automated image pulling from NOAA satellites with an SDR dongle(though in this particular instance I'm taking a shortcut by piggybacking on librtlsdr).
As far as embedded rust. First is what I mentioned earlier - setting up a development environment is an absolute hell, and far not only the first time. You have to go through the same steps each time. At this point I'm somewhat used to it and I can unpack an embedded board and get it going in say 20-30 minutes(having the spec sheet at bay is a must). It also takes an additional layer of tweaking your brain: it takes some time to get used to things such as #![deny(unsafe_code)], #![no_main], #![no_std] and #[entry]. It's nothing like arduinos if you've worked with those, where you plug in the board, open the arduino IDE, select the board model and port and hit "upload".
My experience with getting started with any embedded system, even if I am programming in C, is that it will have a steep learning curve unless someone in the community already went through this and published a how-to.
Thanks for the explanation. I’m planning to go through this book with the recommended hardware. Hopefully, it’s more guided than what you’ve experienced: https://docs.rust-embedded.org/discovery/microbit/
Lots of people pointing out that Rust isn't very ergonomic on embedded platforms. IMO one huge issue is how verbose the syntax needs to be.
Look at some of the cases in the article, such as:
let mut dp = pac::Peripherals::take().unwrap();
dp.RCC.ahb2enr.modify(|_, w| w.gpioaen().set_bit());
In C that would be:
RCC->AHBENR2 |= RCC_AHBENR2_GPIOAEN;
Okay, you only have to take ownership of the Peripherals object once, but what about when they want to set a UART register in a function? Rust wants mutable pointers to have a trail of ownership outside of 'unsafe' blocks, so now we need:
pub fn setup_transmission(regs: &mut R, [...])
where R: Deref<Target = pac::usart1::RegisterBlock> {
[...]
regs.cr2.modify(|_, w| w.stop().bits(config.stop_bits as u8));
[...]
}
This is a bit of a construction, in C(++) we can just do something like:
The Rust snippet seems like it would be safer in a multi-threaded environment like an RTOS, or in cases where interrupts might modify peripheral registers, but...there are so many syntactical details to remember.
I really tried to like embedded Rust, and I still think it compares favorably with the sort of C HALs that most chip manufacturers distribute. But the syntax seems to get in the way of quick simple access to peripheral registers.
I'm not familiar with embedded programming but I don't think this is completely a fair comparison since the Rust implementation is doing error checking, so your C code is missing either some sort of results checking or a macro. The Rust code is also probably presenting some sort of safe API. It's worth mentioning that the C code could most likely be done in Rust with unsafe.
I went through a good bit of the Rust Embedded book, and your assessment is right on the money.
As a long time C programmer who occasionally makes mistakes, I find the type safety of svd2rust-generated APIs incredibly gratifying. When the embedded documentation is inscrutable, the type-safe Rust API prevents you from making whole classes of nasty errors. Especially since the embedded device will probably either fail silently or misbehave in bizarre ways if you mess up.
There may be more elegant ways to design the type-safe API but even having to deal with the verbose syntax it was way easier than stumbling around in C.
The API generated by svd2rust isn't ideal. There's been lots of discussion about ways to improve it, but unfortunately not all that much experimentation of alternate models. So the ubiquitous option generally wins, in a worse-is-better way.
The goal is exactly right: leverage Rust's expressive type system to keep your programs safely composable -- it's a drag trying to debug an issue caused by allocating one peripheral to two different systems. Particularly since the conflicts between peripherals on different models of the same family are sometimes hard to recognize.
Yes, the syntax doesn't have to be this way, it's just the way that the libraries in the ecosystem tend to do it. It works, but it does also have quite a few problems, imho. But since it's just a particular library, you can write your own or use a different one.
Good question! I'm also not a Rust expert, but maybe that syntax would be considered unsafe in some situations?
I think the 'peripheral access crates' try to auto-generate those sorts of struct objects from SVD files, but you still need to obey Rust's ownership rules to get the advantage of its built-in memory safety.
So if a function needs to modify a peripheral's memory, it needs to claim mutable ownership of the relevant peripheral registers. That's a feature of the language which makes it 'safer', but the cost/benefit calculation is a bit different on embedded platforms. They have less memory, and static allocation is usually preferred.
Still, microcontrollers are getting faster quickly. You can even run Linux on some cortex-M4/M7 chips. The verbose syntax might be worthwhile if you're collaborating on complicated firmware.
I'm not sure I agree on the safety aspect - if they only thing preventing unintentional peripheral access is verbosity, that's not really the kind of security that matters, is it?
From my understanding, Rust's HAL APIs are not trying to optimize for ergonomics but for type safety and ownership. It's more tedious to write, but it's also nice to have the standard formatted Rust docs available to see all registers and what you can do with them. It means things like hardware peripherals which only work on certain pins, have those pins defined in the _type system_.
It's more verbose for sure, but I prefer it for it's explicitness and ownership tracking. Definitely not perfect, but it's also very early times for Rust on embedded devices.
In a small embedded system with a 0.1$ single core processor you wouldn't need many of the safety features anyway. Your interrupt routine to read new symbols and your loop processing the content will never collide.
This is the lower end of embedded systems, but the vast majority of them and those don't have any concurrency. Didn't try Rust here, but I would assume you have to jump through some hoops to achieve simple register access or declaring whole blocks as unsafe.
Not seen much point in rust for embedded for small MCU's that I target.
All memory are statically "allocated" as global variables/buffers and never free'd, so there's no ownership or dangling references to worry about ever.
This allows me to know at link time how much memory my program uses.m and of course simplify programming.
Only single cores, the only synchronization issue is between interrupts and main program, a case rust doesn't handle afaik(?) and in 99% of the cases a ring buffer or simple flag variable is enough.
Never use much abstractions, so C is fine in terms of ergonomics.
Never use third party libraries aside from CMSIS register headers.
Probably the only thing that would help me is some static reflection and compile time programming, but I have python scripts as a code generation step to generate tables, definitions, annotations, etc, and that works fine.
> the only synchronization issue is between interrupts and main program, a case rust doesn't handle afaik(?) and in 99% of the cases a ring buffer or simple flag variable is enough.
I'm not sure what you're expecting it to handle that it isn't. That static flag or ring buffer needs synchronization and rusyc will absolutely not let you forget that (in safe code).
One technique you get in Rust that's very compelling for embedded is using traits, especially when implemented on zero-sized types, to cleanly express configuration. Personally I abhor all those one-off table generating scripts.
Damn, I've been doing a bunch of exploring of this exact topic over the past week. This would have saved me a bunch of time. Unlucky for me it wasn't submitted last Sunday!
One thing that's killed me a bit is the core::/std:: split, when the core:: versions have the exact same semantics, or perhaps are equivalent for the things that they do implement (for example, the core version might be missing the serde parts, but is otherwise the same). There's a few crates I tried out that wouldn't compile with no_std, but with a simple text substitution, would work. I'm sure there's a million edge cases I'm not aware of, but if cargo or rustc could provide an option to just sub in the core version, that'd be really nice.
I assume one could send a patch to those crates that defines “use core::” instead of “use std::” by using the compile time config directive to check whether the std is enabled?
Oh yes, and I've started preparing those patches. It's just a bit of a bummer to have the churn when you're just starting out and iterating through ideas.
If you disable stuff with `cfg`, you have the ability to give cleaner error outputs. When designing a library to allow choosing between one of two dependencies for an async runtime, I conditionally compiled out all of the crate except for one of two invocations of the `compile_error!` macro (https://doc.rust-lang.org/std/macro.compile_error.html) if either both or neither of the dependencies were enabled, which I think made the experience for users of the library much better compared to having to figure out what was going on from pages and pages of "not found" or "already defined here" errors.
In addition to Cliff's talk/blog -- which are absolutely outstanding -- I would recommend listening to the Twitter Space we did on Hubris and Humility last week.[0] It was a really fun conversation, and it also serves as a bit of a B-side for the talk in that it goes into some of the subtler details that we feel are important, but didn't quite rise to the level of the presentation. And of course, be sure to check out the source itself![1][2]
I've been doing embedded development for over 20 years. The rust evangelists just don't understand embedded development.
Embedded developers are very conservative, they don't chase the shiny and believe in using old tried and true technology. C++ offers significant benefits over C in the embedded space and its been impossible to gain traction.
With C you have a HAL provided by a vendor that allows very rapid bring up of the hardware which is 90% of the battle in embedded. Switching to Rust you would have to write your own.
With C you have billions of lines C libraries at your disposal to bring your product to market. To use Rust you would have to write your own or deal with the shims and lose the Rust benefits.
I do 4-5 embedded projects a year, using Rust and not having the HAL and C library ecosystem would put those projects ad a competitive disadvantage.
I find the whole hubris and humilty thing is ridiculous. Writing a RTOS in Rust which isn't your core competency is such a waste of resources. Use FreeRTOS and ship the product.
I concur, and add my 2 cents on why "Use FreeRTOS and ship the product" actually works in the real world (i.e. paid job), from the perspective of a part-time firmware engineer. Obviously none of this applies if you are just having fun building your side project.
1. Unless your product is mass-produced, as long as your $work has >= 2 projects, having them share a over-spec'd MCU family (i.e. way more pins, MHz, SRAM, ... than you could imagine yourself needing), or even a single part, is very desirable. The BOM is much simpler, and given the current chip shortage situation, you don't have to track incompatible chip families. All the infra work, internal codebases, know-how, even quirks and their workarounds can be shared in-house.
2. The vendor most likely has some BSP (board support package) written in C with examples in C as well, and it most likely will support / assume / integrate with FreeRTOS. Neither of these will be optimized to the bones --- usually the situation is quite on the contrary, with O(n) loops liberally littered around. However, since you picked a overkill MCU, it will more likely than not gladly chug along, given you don't abuse them. When you FFI / shim / start from scratch with registers, usually the happy path does work after some hacking, but when it doesn't work, do you have confidence it's a HW issue, or is it rather your added indirection? With Embedded Rust's prevalent macro-heavy approach, this can be a real time sink.
3. Firmware engineering really works closer to hardware than you'd think. To avoid a catch-22 situation in the co-design, already knowing a lot about what embedded platform breaks the loop and effectively decouples HW and SW, reducing your time-to-market.
From your statements it seems to me you haven't really given Rust an honest evaluation.
I've been using the Rust stm32 HALs offered by the open-source community and they are stellar. People are doing some really great work so people like myself can come along and knock together a highly performative bare metal application with relative ease. Obviously not all features are supported and some chips are more supported than others but its a work in progress.
Regarding library support, Rust has crates.io and a first class package manager with native support for heapless environments which means that I can easily add a package designed specifically for embedded development to my project. Its pretty awesome not having to mess around with a million linker flags, copy-pasting directories etc to try to get a library to work.
Ontop of that with Rusts language features there are standardized traits for peripherals like I2C, SPI etc, meaning that any library can easily consume any HAL's peripheral implementation. Basically I can install an LCD library and have it running with 1 line of code.
Obviously there will be more C libraries out there and it will take time to port them over to Rust but the foundation is very very solid.
That's a rebuttal for your HAL and library criticisms and I still think there are vast safety and ergonomic improvements to using Rust over C which would undoubtedly lead to more reliable code and quicker development times.
Do you think there's any potential for rust to gain a competitive advantage in safety critical systems?
Or are the benefits too difficult to quantify?
It seems like if something along the lines of "MISRA C" (but requiring Rust) were to become an industry best practice, we might see the Rust ecosystem develop.
However, automakers are some of the most conservative companies out there when it comes to tech, and I doubt they really care about safety or formal correctness as much as being able to slap a "MISRA" sticker on the code for liability reasons.
It would have to be, decades I imagine, before there's any chance of rust becoming truly widespread in the embedded world. Universities would need to start teaching with it. People who know C and nothing else would need to retire. I agree that it's unlikely to be commercially viable at a large scale any time soon (unless you can find a handful of true believers for your team).
> Do you think there's any potential for rust to gain a competitive advantage in safety critical systems?
There already is a solution for heavily safety-critical systems and it's got 40 years of industry track record it's called Ada. [0]
I don't mean to sound dismissive but there is such a treasure trouve of battle tested industry learned experience in the Ada standard, it really is worth learning and considering.[1]
Ada has had four standards in almost four decades. For anyone in industry this pace is adequate and all of those standards were truly value adding without any disruption to legacy systems. This is not compatible with the pace at which most Systems and Backend Rust enthusiasts want to move forward.
I don't believe the two end applications/motivations can be merged under a single language or ecosystem despite many similarities.
How do you go about learning Ada without working in aerospace or a defense contractor? I've never organically run into any software in my daily life or using OSS that it is written in it. I know some exists, but it seems to be an incredibly isolated island disconnected from any of the other major programming ecosystems: web, HPC, games, CRUD business apps, finance/trading, embedded (outside aerospace/defense) etc. I don't know of a single major project in any of those fields written in Ada. I work in finance and have interviewed hundreds of programmers working on systems moving billions of dollars around at this point and not once have any of them ever mentioned having worked with it. I am skeptical they have as much to learn from Ada as vice versa, since Ada seems cut off from the majority of brains.
I see benefits of it, but I think its a hard sell. In aviation things need certified, you can buy off the shelf compilers, RTOSs, and libraries certified for aviation. Rust doesn't have any of that.
Medical devices, maybe, but you still have the problem of finding talent. If you were to build a product on Rust, you would have trouble hiring people to maintain it over the lifecycle of the product.
Weirdly, it's already significantly easier to find Rust (or Rust-interested) developers these days than C/C++. If your main concern is being able to hire devs then Rust is a clear win.
And don't sleep on Sealed Rust. They've surely got a hard road ahead, but there are some dedicated folks working on it.
Yeah I call bullshit. By sheer numbers C/C++ programmers are much easier to find. Rust may improve your company's appeal to an interested programmer, but by the numbers C/C++ have incredibly greater supply.
> C++ offers significant benefits over C in the embedded space and its been impossible to gain traction.
Wasn't it Arduino that tried to establish C++? I could never see the advantages to be honest, but I didn't really use too much C++.
Header files mainly provide semantic descriptors to otherwise pretty hard to memorize memory addresses. A format like SVD would provide the main benefit for other languages though. Even non-ARM-processor manufacturers use it.
I believe many embedded devs long for "high tech features" like unit tests, which of course would require some form of formal simulators and peripherals. That problem goes far beyond the choice of language though.
Validation is an extremely hard thing to do for embedded system outside of a specialized laboratory. Memory safety is almost laughingly trivial compared to that.
Vendor-provided SDKs are typically not good. Nordic's is one of the more decent ones and it still has a messy makefile based build system that's horrible to use. With nrf-rs you just use it as a cargo dependency.
You don't have to write your own HAL when the community maintains really excellent ones, written by people who actually want to fucking use it rather than vendor employees who just have to get it out of the door.
I've been in the embedded space for over 10 years so I figure I'll give my thoughts on the matter. Apologies up front as this will be a little ranty.
> Embedded developers are very conservative, they don't chase the shiny and believe in using old tried and true technology.
This is underselling the point. They are conservative in the same sense as an electrical engineer who refuses to use ECAD software or transistors because drafting tables and vacuum tubes are tried and true. There's a difference between "[not] chas[ing] the shiny" and just being obstinate.
> With C you have a HAL provided by a vendor that allows very rapid bring up of the hardware which is 90% of the battle in embedded. Switching to Rust you would have to write your own.
I'm not sure why vendor supplied HALs - which can be pretty hit or miss in terms of quality - are so important for board bring-up. In the end it's a minor milestone in the development lifecycle for all but the simplest of projects. In the projects I work on practically all of the time is spent on the complexity of the firmware and how it interacts with the various networks of sensors, actuators, cellular, BLE, storage, etc. Besides, last I checked there were many people putting effort into HALs written in Rust.
> With C you have billions of lines C libraries at your disposal to bring your product to market. To use Rust you would have to write your own or deal with the shims and lose the Rust benefits.
First off it has not been my experience that libraries are used often. As you mention later FreeRTOS is generally the only external code I see pulled into a project, everything else is written from scratch or copy pasted from a previous project which was written from scratch or copy pasted from a previous previous project etc. Let's assume that libraries are commonly used though, how exactly do you use them? This is one point of C/C++ that is still absolutely dire. Rust has Cargo whereas C/C++ has a lot of wacky ways to deal with it such as through git submodules, or cmake, or possibly conan. Generally speaking what I see is the code being copy pasted into the project and never updated despite the growing security concerns for connected embedded devices. Of course Rust offers more than mere package management, Cargo is also the build system, it also has support for automated testing and documentation. All of these things are typically afterthoughts at best in many embedded projects.
> I do 4-5 embedded projects a year, using Rust and not having the HAL and C library ecosystem would put those projects ad a competitive disadvantage.
To be clear at this point in time if I were asked if an embedded project should be written in Rust my answer would be "probably not", although given 5-10 years that answer very well may change to the affirmative. The ecosystem is young and growing; as long as the growth continues it will likely be a viable alternative in the future.
> Of course Rust offers more than mere package management, Cargo is also the build system, it also has support for automated testing and documentation. All of these things are typically afterthoughts at best in many embedded projects.
Completely agree. I'm starting new embedded projects in Rust mostly because cargo test is a force multiplier. (well and also the expressive type system)
Oxide Computer is a startup without a product on the market yet.
Embedded operating systems are a solved problem, there are many available on the market used in mission critical systems.
At most companies that are trying to ship a product and have a mountain of work to be done, and someone said, "I know, lets build our own RTOS", even though there are already many perfectly fine ones out there, they would be laughed out of the room.
I encourage you to learn more about Hubris and about how we arrived at it -- certainly, it was nothing as you convey (and to the contrary, we tried hard to get extant systems to work). Read Cliff's blog[0] for details, but we are not lightweights and nor are we rookies, and the ideas represented in Hubris come from decades of our collective experience at the hardware/software interface.
I also emphatically disagree that "embedded operating systems are a solved problem" (or indeed, that operating systems are a solved problem) -- and that in general such proclamations reveal much more about the technologist than the technology that they proclaim to be finished.
I do quite a bit of good old C on Atmel & STMicro chips M0+ chips. When I do, there’s a whole layer/framework of register definitions (structs, enumerated, typedefs, etc) C header files that I’m able to include for my specific chip and package.
If I want to experiment with Rust in place of C on one of these chips (say an Atmel SAMD21 J) what are my options? Do I have to reverse engineer all of those register mappings into Rust constructs? Can Rust import and use C header files?
To add on to what bri3d said, these Rust register definitions are being auto-generated using SVD* files published by the chip vendors. For stm32 for example there are the auto-generated register definitions** and then the HAL layers*** on top that try to build easy to use tools on top of the registers (e.g. an SPI or USART type with write and read functions).
I am not terribly well versed in Rust MCU development, but I poked around with the teensy-rs and esp32-rs projects a couple years ago.
ARM seems to provide some XML register description files called SVDs[0]. They seem to do a decent job describing the registers and peripherals in a way that allows code generation tools to create a register access library, upon which higher level HALs can be built.
For an example, you might look at the esp-rs project on GitHub. The lowest level, if I recall correctly, is the Peripheral Access Crate [1]. The README for this project describes a bit about the SVD file, and svd2rs code generation tool. Sometimes the SVD file needs to be patched to produce high quality Rust bindings.
This PAC is then required as a dependency of the higher level HAL for the esp32 [2]. The higher level HAL also implements some traits from a shared Rust HAL effort [3].
I'm not really an expert in Rust so if I've described any of this wrong, someone please correct me.
I have developed firmware for Atmel chips. AT90USB1286 for example. Their IDE is nice, libraries are great. Porting switching to Rust just for the heck of it I think is absolutely not worth it. And the type of safety Rust adds for that type of job I think is overvalued. My firmware for MCU works for years and no bugs reported. I can't formally prove that they do not exist but what do I care. I am a practical man. If customers complain and I loose revenue I would look for the reasons why did it happen to avoid repeating. If they don't - I have better things to do than waste my time on yet another language where it does not offer me tangible benefits. I am my own business and programming languages to me are just tools. I do not work to program in particular language. I work to make products I want and have clients pay for it.
C had years for tooling to be developed. Currently I still prefer C and find Rust to be too strict in parts. But I guess when better tools are available, it could be a decent choice.
But for processors this small I currently could not tell you any advantages to be honest.
>"But I guess when better tools are available, it could be a decent choice."
When / if then sure (assuming it would also be compatible with the megatons of very useful libraries written in C). I would have very little desire to replicate something like LUFA for example.
I'm an amateur doing Rust stuff on SAMD21 (Trinket M0) using the native bindings mentioned in sibling post. It's workable if you start from the examples and evolve from there. Things are still in shift, the ecosystem is very young, so there are areas of API instability, version conflicts or missing features when you try to integrate with other embedded projects (like defmt, cargo-embed or embassy). STM32 support definitely has less rough edges, I'd start there first (e.g. with a bluepill board) and then move to SAMD later.
Yes, after piping them through rust-bindgen, any binary can be statically of dynamically linked. I did this, it's nice that it's possible, but the PAC route others have described here is way better.
I recently did an embedded project using rust (https://yager.io/vumeter/vu.html). It was great, and I'm planning to use Rust for embedded projects in the future. There are a few pain points, especially around rust's relatively limited polymorphism, mutable refs, and the constraints imposed on async/await by type system limitations, but it was still super nice compared to using C(++). I'm also optimistic these pain points can be mostly fixed with improvements to the type system. I think they might already have improved length polymorphism - I think I saw something about that recently.
As someone who plans on dabbling into embedded Rust next, this is great to hear. Thanks for sharing. Any resources that were helpful in your embedded Rust journey?
> Hiring programmers proficient in Rust is much more difficult than in C or C++ due to the smaller user base. Example code, tools, and libraries released by MCU manufacturers are almost always for C or C++. The Rust embedded infrastructure is new, and rapidly changing.
While Rust cost per engineer today is higher, is the cost of the project lower, because due to Rust those engineers are much more efficient?
How should I get started with writing embedded firmware period? I am a SWE but only have professional/personal experience with web development (including backend services). As a start, I have an old Chinese mp3 player that would be really neat to be able to hack and make some changes to. How would I start with that, if that's at all a good starting point.
As suggested, one path is to port Rockbox firmware for your MP3 player. However, since you do not have any prior experience in embedded domain, it will not be a easy path.
I would suggest that you try using the Arduino or RaspberryPI hardware. Both of these platforms are widely used, so you would be able to easily get help if you get stuck. Also, these are 'simple' platforms that are widely available, so the learning materials available is unmatched.
Building a simple project like a weather station or clock will keep you focused on a "actual" target/goal.
If your focus is to just revive/upgrade it with more features, rockbox[0] might be a choice for you. It is open source so if that works for your model, then you may be able to get to hacking quicker than doing it all by yourself (though the latter might be a lot more satisfying).