|
C/C++ embedded developer here. I've never used Rust in embedded because I didn't really see the need. But I'm currently taking a retreat and I started playing with embedded Rust. At work I had the same stance as you, and pushed against adding rust to our ecosystem (to avoid fragmenting what was 100% C++/python):
- memory ownership bugs are not a problem (and even on the host, with unique_ptr and shared_ptr you can really get quite far)
- C++ meta programming is really quite expressive to nip most bugs in the bud (say, writing to the wrong port, adding an i16 to an i32, or adding ms to us),
- C++ meta programming is pretty good at building bigger abstraction, such as monadic tasks Here's the main advantages I see and which convinced me to take it seriously. - cargo for package management and building. It's extremely easy and "nice" to add packages, manage multiple configurations, build additional tools as part of the building, but run them on the host (say, a protocol parser generator etc...) This is just huge. I basically almost never reused any code except copy pasting source from other projects or from the vendor lib straight into the project, because anything else was just too brittle, even with CMake. Most embedded projects I worked on had their own idiosyncratic build system based on make, and you had to relearn it every time. - macros that are actually worth it. THis might be the most exciting thing. I often use patterns such as state machines and other formalisms, but the best I can do in C++ to make them nice to write is mix up some ugly ass macros with some templating, and it always ends up being a mess in the error messages. Rust gives you some really decent "lisp"-y metaprogramming. - rust works equally well for the bare metal and the highest level scripting. That means that my projects won't end up being a mix of cmake + bash + python + C++, I can do everything in rust. - the embedded code with an abstracted HAL looks REALLY nice. It's almost arduino-like, except this is actually the real thing. This is what my pairing partner and I came up with to control a SPI display: fn new(
spim: spim::Spim<SPIM0>,
timer: &'a mut hal::Timer<pac::TIMER0>,
cs: gpio::Pin<gpio::Output<gpio::PushPull>>,
rst: gpio::Pin<gpio::Output<gpio::PushPull>>,
dc: gpio::Pin<gpio::Output<gpio::PushPull>>,
busy: gpio::Pin<gpio::Input<gpio::PullUp>>,
) -> Display<'a> {
return Display {
spim,
timer,
cs,
rst,
dc,
busy,
};
}
fn init(&mut self) {
self.reset();
// BOOSTER SOFT START
self.spi(&[0x06u8, 0x17, 0x17, 0x17]);
// POWER ON
self.spi(&[0x04]);
// CHECK NOT BUSY
self.check_not_busy();
Not only is every GPIO configuration typechecked, but the HAL layer takes care of initializing the abstracted HAL peripheral correctly for this chip architecture (nrf52833). This is of course not rocket science, but dang it just felt nice to have it work, and not have to wrestle with some mud-tier vendor HAL monstrosity.- the community has reached critical mass, and I think it won't be too long until there are actually more rust developers on the market than C++ developers. Plus you kind of get the full-stack experience. |