7 ms·
Embedded developer here, ARM Cortex-M4 microcontrollers. I've been keeping an eye on Rust for the embedded space and although there has been a lot of movement i
by _sh 9y ago
Embedded developer here, ARM Cortex-M4 microcontrollers. I've been keeping an eye on Rust for the embedded space and although there has been a lot of movement in that area--particularly in the last couple of months--I'm not sure the value proposition fits the microcontroller market, where of course C is king.
While Rust has much to offer as a programming paradigm in general, the main value is in the borrow-checker (the linked transcript cites 'memory bugs' as the most common class of bugs). Embedded software practitioners long ago abandoned dynamic memory allocation and with it the 'use-after-free' and 'out-of-bounds access' bugs, instead re-defining the problem as one of latency (e.g. you'll need to process that static buffer before it gets re-used by your UART interrupt). Take away the borrow-checker, and Rust looks less compelling.
In time, Rust will find its niche in the embedded space, most likely occupying the high-level RPi/BBB/iMX SoC layer and perhaps working its way down to microcontrollers. As wiremine points out, it will require vendor support--moving away from your vendor's toolchain is a world of hurt that seasoned embedded developers just won't even consider. Pragmatism reigns: time-to-market and a cheap BoM are the main metrics, programming language a distant 10th.
- pknopf 9y agoWhy is C king? Why not C++? The difference is just a compiler, and you can use C in C++.
- dingo_bat 9y agoI don't know why. But it's true.
- bb88 9y agoHere's a rant by Linus Torvalds. Take the politics and snark with a grain of salt. His technical arguments, however, are spot on. http://harmful.cat-v.org/software/c++/linus http://harmful.cat-v.org/software/c++/linus
- Inityx 9y agoI'm not sure how much water his argument here holds anymore. C++ has changed A LOT since 2007; idiomatic C++11 is an extremely different language from C++03, and C++20 is almost unrecognizable to an early C++ developer.
- bb88 9y agoI bet you $5 if you ask him, he'd probably say the exact same thing.
- scottlamb 9y agoHe writes C++ now, so I imagine he'd say the language has its place. https://en.wikipedia.org/wiki/Subsurface_(software) https://en.wikipedia.org/wiki/Subsurface_(software) (He'd probably still say C++ is inappropriate for the Linux kernel, though.)
- bb88 9y agoI was going to honor my bet here, until I did a little googling and found this during the discussion of when they ported subsurface to Qt. "A word of warning: Linus has very strong feelings about all the things that are wrong with C++ and at times has been known to be less diplomatic than me when explaining his point of view... :-) But he made a clear statement that he is interested in seeing this port happening, as long as most of the program logic that is not UI code stays in (quote) "sane C files". So please keep that in mind as we drive this further." http://lists.subsurface-divelog.org/pipermail/subsurface/2013-March/005365.html http://lists.subsurface-divelog.org/pipermail/subsurface/201...
- Analemma_ 9y agoI’m not an embedded developer, but my guess is that if they’re not even using dynamic memory, I doubt they need or want anything that C++ has to offer.
- pknopf 9y agoI can see people arguing for object oriented over procedural. As I type that, I realize I've recently learned to love Go, so maybe it is my bias.
- tarmon 9y agoThere is still a lot to be gained by strong typing even in projects where everything is statically allocated.
- jwhite 9y agoThat's more a matter of experience and attitude -- even simple things like reference types are nice. Also, templates offer a lot of abstraction power that can be used to model the hardware nicely, without sacrificing efficiency. Many embedded programmers come from a background that doesn't expose them to those sorts of ideas though.
- carlmr 9y agoCan confirm, C++ has some nice features which I'd even like without malloc. OTOH I'm already horrified at the code quality problems pretty much every embedded shop faces. C++ would only make this matter worse. As a software inclined embedded guy I also often think of what would be possible if we switched to C++. But then I think of what's probable.
- bb88 9y agoMany people need in memory databases and linked lists (or binary trees) to hash information and sort it. There's a case to be made for an OO style program where I create an object and give it a chunk of memory to manage a B-tree, so I can keep my memory from being fragmented, but using C++ for that is serious overkill.
- 9y ago
- Inityx 9y agoC is king because the industry is currently dominated by people who have been doing this since before C++ was a thing. Additionally, most of these people are primarily electrical engineers, and don't have as strong of a background in computer science. They've been using C for decades and it does everything they want, why would they take the time to learn the boundless complexity introduced by a language that offers them (what they perceive to be) very little? Talking to long-time C programmers about C++ is actually surprisingly difficult: https://youtu.be/D7Sd8A6_fYU https://youtu.be/D7Sd8A6_fYU
- pknopf 9y agoLet me ask it this way: I'm a higher level programmer. Later in my career I happen to get down to microcontrollers, what would stop me from using C++?
- jwhite 9y agoNothing. I've worked on Cortex-M4 projects in C++. It's nice in many ways. The people working on the project had a much more diverse background than the typical EE who learned C as an undergrad mentioned in another thread.
- q3k 9y agoThe standard library (with all its' duplicate code resulting from hardcore templating) will blow up your flash space usage significantly, to the point where you will run out of it sooner than you expect. You will spend time finding alternative standard libraries that are size-optimized and you might end up rewriting a lot of what you take for granted in your C++ daily usage. For example, the Arduino environment is C++-based, but it's not anything like on the desktop due to it not shipping an std:: . Your typical heap-happy usage will not go down well on a microcontroller, either. Having very constrained RAM makes heap fragmentation much more of an issue.
- atilaneves 9y agoThen don't use the standard library. Don't even link it in. > Your typical heap-happy usage Huh? 1990s C++ was typically heap-happy, which is part of the reason Java looks the way it does. Idiomatic modern C++ uses the stack as much as possible. And one can use custom allocators.
- mikekchar 9y agoIt's more difficult. I used C++ in many projects for years and enjoyed working with it. You have to be very disciplined, though, and you need to know a lot about how it works under the hood to get it to play nicely, but it has it's niche. Embedded is not it's niche IMHO (and I've also done embedded, where we specifically chose to use C over C++). Really, whenever you are doing any kind of embedded or real time project you are basically doing "resource limited development". The resource can be memory, I/O, CPU or any and all of that (or more). You need to be able to control exactly how it's used. C++ is often used to abstract you away from those things -- which is exactly the opposite of what you want. C is a high-ish level language that is close enough to the metal that you can fairly easily understand the implications of what you are doing. C++ is not and it's incredibly easy to build a monstrocity that chews memory -- not just working memory, but application size too. Even dealing with name mangling is a surprising PITA when you are dealing with embedded -- remember embedded means you often have to build your own tools because nobody else is using your platform ;-). Like I said, I actually like C++ (or at least C++ of a couple of decades ago -- the language seems to have changed a lot since I last used it, so it's hard for me to say). There are a lot of times where I simply don't care about controlling resources to that level. These days there are a lot of other choices and I'm not sure that I would ever choose C++ for a project again, but definitely back in the day it was something I reached for quite a bit. WRT Rust, I agree with the OP that the borrow checker is really nice. I recently spent some time playing with Rust to see how easy it was to implement higher level abstractions. One of the things I was really impressed with was how hard Rust slaps you when you try to do something that would explode your memory footprint. It still feels a bit immature to me, but it has tremendous promise (and if you don't mind working around the immaturity, it's probably fine to use at the moment).
- tluyben2 9y agoI find it easier to use C and assembly on very constrained devices becauee I know what the output will be; with C++ it is less clear. If you write code that has to fit in 24kb, you need to think about what every instruction looks like after compilation and that just is far easier with C and (obviously) asm in my experience.
- pjc50 9y agoAbout 15 years ago I did some PIC16 programming immediately after a lot of C++, so I tried working in a C++ style. The first obstacle was that there was no C++ compiler. So I wrote some very C++ style C: nice little structs with associated functions for mainpulating them, which took a pointer to struct as first argument. The code did not fit in the PIC. It turns out that the PIC16 lacks certain indirect addressing modes, so every access to a structure member from a pointer turns into a long sequence of instructions to do the arithmetic. Oh, and this particular chip only allows you a maximum stack depth of 8, so you have to ration your use of utility functions. The compiler is bad at inlining so macros are prefereable. By the time I had finished it was an extremely C program with no trace of C++ style at all. The situation has got a lot better but there are still limitations which will trip up the unwary. And one day someone's going to point out that they can save $0.50 on every one of a million devices if you use one of these tiny chips with no indirect addressing and limited stack.
- s73v3r_ 9y agoA lot of it is due to the fact that many of these chips use ancient compilers, so the C++ support isn't that good.
- pcein 9y agoIt is not just the borrow checker. The ownership system / move semantics and powerful static type system with traits and generics helps to create some interesting abstractions (which have very little run time overhead) - pleas check: http://blog.japaric.io/brave-new-io/ http://blog.japaric.io/brave-new-io/ as well as other articles on that blog.
- wallacoloo 9y agoAlso working in the same space. I had the opportunity to evaluate Rust for our development environment back in late September. The killer feature it offered us is serde - rust's general purpose SERializer DEserializer library. So much of our code is centered around taking measurements with a bare metal system and then transmitting them to a linux box for processing. Being able to just write `#[derive(Serialize, Deserialize)]` above a struct and then be able to send/receive it across the channel via `rpmsg.send(mystruct)` or `let mystruct: MyStruct = rpmsg.recv()` is magic. Furthermore, by encapsulating each possible message type as an enum variant, match statements provide a really great way for dispatching the message to the appropriate handler after we deserialize it. As for the borrow checker, I actually did find it useful in bare metal. But more for handling hardware resources. Different measurements require different sets of power supplies to be activated, and exclusive control over different I/Os, etc. The ownership model made it easier to ensure statically that we sequence the measurements in a way such that the different measurement routines can never reconfigure resources that are in use by a different routine. Anyway, we sadly aren't using Rust yet in production, even after that. Holding off until we start the next product.
- Matthias247 9y agoWhat data format do you use with Serde on embedded? JSON? I read somewhere that Serde works in no-std environments, but wasn't sure whether it does that with all possible data formats.
- wallacoloo 9y agoSerde is middleware, which really just shuttles calls between a serializable object and the serializing backend in a standard (and performant!) way. That middleware works in no_std environments, but not all backends do. I'm not up to date on which backends support no_std, and which backends support no-alloc - some backends support no_std but require an allocator. When I looked into this in Sept, ssmarshal was the only general-purpose backend I could find that supported no_std & didn't need an allocator. There was some talk of adding no_std support to bincode - looks like it hasn't gone anywhere: https://github.com/TyOverby/bincode/issues/189 https://github.com/TyOverby/bincode/issues/189 My one gripe with ssmarshal is that - in Sept - it would refuse to serialize collections whose size isn't compile-time constant. Obviously, you aren't going to be serializing Vec, Map, etc, in a no_std environment. But one could very well wish to serialize stack-allocated equivalents (e.g. arrayvec, where you have a vector that stores all data on the stack and grows up to the space allocated for it). In order to serialize an arrayvec, I had to write wrapper code that serialized the entire underlying fixed-size storage, regardless of how much was actually in use. Things move fast in rust-land - ssmarshal might have a feature that allows serializing dynamically-sized types, or there might be new/more versatile backends since Sept. I think the most difficult thing about deserializing JSON in a no-std environment is that strings can have escape sequences. So when you deserialize a string, you can't just pass a reference into your buffer up to the frontend - you have to decode the string. Usually one would heap-allocate in the backend for that, but if you have the ability to mutate the buffer you're deserializing from, I don't see any fundamental reason why you couldn't decode the string in place and then yield it - I'm pretty sure all encoded JSON strings are at least as long as their decoded version. The easy alternative is to deserialize the string as a [Char] sequence (i.e. pass it to the frontend character by character) and let the frontend worry about memory management, which isn't even necessarily so bad, with things like ArrayString.
- steveklabnik 9y agoThe borrow checker is not inherently about dynamic memory allocation. Heck, my toy x86_64 OS doesn't even have an allocator at all yet! Rust's features, including the borrow checker, are still useful here. I really like https://os.phil-opp.com/page-tables/ https://os.phil-opp.com/page-tables/, for example, which talks about using Rust's type system to ensure safe page table usage.
- Matthias247 9y agoYou don't need dynamic memory to make a ton of memory errors. There's still lots of possibilities to have dangling pointers to somewhere else on the stack, memory issues with objects pools, memory issues due to race conditions in multitasked RTOS systems, etc. On top of those there's misunderstandings whether a char* is actually a pointer or an array, misunderstandings who knows those, etc. I've seen enough of these issues in an RTOS project to believe that Rust (and even modern C++) will be a huge step up in overall quality and productivity.
- pcwalton 9y agoJust that Rust is known for its borrow checker doesn't mean that the borrow checker is the only type of safety that Rust offers. The old standbys are still valuable: bounds checks, null pointers, compile-time data race detection. > Pragmatism reigns: time-to-market and a cheap BoM are the main metrics, programming language a distant 10th. I find that I can develop software faster with Rust than with C, simply because of language and standard library features: closures, iterators, a real string library, a vector type, hash tables in libstd, better unit testing support, etc. Development speed can affect time to market. As the industry matures, though, reliability generally becomes more important. And "embedded" covers everything from IoT light bulbs (correctness less important…for now) to avionics (correctness extremely important).
- shaklee3 9y agoYou should be comparing to c++ and not c, since if you are looking for those features, you'll find them.
- reificator 9y agoEmbedded systems typically doesn't use C++ either. I think it's still a fair comparison.
- andromeduck 9y agoIn my experience is usually a mix of both.
- throwaway84742 9y agoNull pointer is actually perfectly fine on bare metal. There’s no memory protection, so it just points to address 0x0, and if you deref it nothing bad will happen.
- unwind 9y agoThis is not true at all. First, of course, there is no requirement for NULL to map to address zero. Second even if you do en uo there, many architectures don't even have memory at 0x0. Spurious writes are spurious writes regardless of whether or not you get a fault. You are still not doing what you want to be doing.
- carlmr 9y agoYou might want to check out https://blog.rust-lang.org/2015/04/10/Fearless-Concurrency.html https://blog.rust-lang.org/2015/04/10/Fearless-Concurrency.h..., it goes into other types of concurrency in Rust, some probably more interesting to embedded devs than the general dynamic allocation stuff. The key takeaway is that the borrow checker isn't only good for dynamic memory allocation in GCless environments (in general even the stack allocated variables which we have in embedded profit from the borrow checker). The other thing is that Rust offers a lot of very useful abstractions (e.g. enums with data) and a strong type system, which is sorely lacking for C. The embedded systems industry is one of the slowest industries to adopt to new technologies, so I'm not holding my breath, but I think everything is there in Rust to make it a good embedded language.
- jononor 9y agoThe type system is orders of magnitude better than C, which can be used to reduce bugs.
- jononor 9y agoThe borrow checker should be quite useful when using tasks and shared resources in an RTOS. Just closures alone is a benefit.
- koffiezet 9y agoYour compiler adding the execution of an unpredictable amount of CPU instructions at certain points, which can change with new compiler versions? That sounds like an nightmare for realtime applications. Yes, I have been in situations where I had to count the instructions and clock-cycles to meet deadlines.
- lvh 9y agoWhat feature adds that? The borrow checker, lifetimes and ownership are all compile time concepts in Rust.
- c3833174 9y agoCompile time doesn't mean the compiler won't add more instructions where you might not expect them.
- steveklabnik 9y agoThat's true of any compiler of any language.
- lvh 9y agoAre you suggesting the borrow checker adds instructions? I feel the meaning of my comment was pretty clear to mean “strictly compile time”.
- jononor 9y agoWhy do you think this will be a problem? I mean any more than with any optimizing compiler (C etc).
- emilfihlman 9y ago>As wiremine points out, it will require vendor support--moving away from your vendor's toolchain is a world of hurt that seasoned embedded developers just won't even consider. I wont consider a chip/microcontroller if it doesn't support open source command line tools. Vendor support is technological debt that hinders every bit of testing and automation.
- koffiezet 9y agoI used to work in a space that was once considered to be embedded (POS systems, including some based on low-cost 8bit controllers, m86k based stuff and low-end ARM at the end), and got out of it just before the entire thing was taken over by fast 32bit or even 64bit ARM CPU's, which eventually moved to running stock Linux or Android. As ARM chips become cheaper and cheaper, the low-level embedded fields will shrink further and further, and the line will shift more and more towards running a full-blown OS below the actual applications. I've even encountered a platform marketed as being "realtime" which was running Linux. It will always be there, micro-controllers are dirt dirt cheap, and in many cases more suited for industrial environments, and realtime will always be there, but it will become more & more specialized. And as you say, once you get down to a certain level the borrow-checker is a useful feature so I expect it won't be such an interesting target for Rust, and this will probably remain C's stronghold.
- petra 9y agoAnd even where Linux won't go, mbed os and it's ecosystem could take - as long as the requirements for faster time to market will continue.
- epage 9y agoTo me, the power of it isn't just in dynamic memory but also in that Rust has clear move vs ref vs bit copy vs logical copy (clone) and use-after-free protection. As one example of the benefit of this. This makes me feel a lot more comfortable writing state machines in Rust's type system. See https://hoverbear.org/2016/10/12/rust-state-machine-pattern/ https://hoverbear.org/2016/10/12/rust-state-machine-pattern/