4 ms·
Bugs that occur due to dynamic memory allocation and freeing are definitely not a concern for 99.99% of well-written embedded software. We avoid dynamic memory
by notalaser 10y ago
Bugs that occur due to dynamic memory allocation and freeing are definitely not a concern for 99.99% of well-written embedded software. We avoid dynamic memory tricks not because of memory size constraints, but because it makes verification difficult and introduces additional uncertainty in the program's behaviour and timing. Even in systems where there's plenty of memory to support it, dynamic memory allocation is done sparingly -- sparingly enough that it can be properly managed via code reviews and the like.
Real-life, but somewhat extreme metric: in my last project, there were two instances of dynamic memory allocation, in about 100k lines of code, and both were in third-party code (we could have replaced them but you know, not broken, nothing to fix). At the other end of the spectrum, I think we had about one dynamic memory allocation at every 6-700 lines of code, but almost all of them were confined to a single scope, and they were largely due to code smell. (Edit: I think at one time we got really pissed when we saw how out of hand it had gotten and we went back and replaced most of these occurences, but I don't remember the metrics.)
In any case, lifetime management at the lexical level should not be a problem in most embedded applications, because there should not be much lifetime to manage. By "lexical level", I mean "things that are exposed to the language and manipulated through its primary means" -- variables, functions and so on. (Edit: there are legitimate exceptions to "should not be much lifetime to manage", but they are very few; the non-legitimate ones are in code of such poor quality that Rust won't help: what those codebases need isn't a new language but different managers, different programmers, or both.)
Unfortunately, this retains a whole class of errors that, to my knowledge, Rust (or any other language, frankly) can't manage. Off-by-one errors, for instance, can be handled if they occur at the end of the buffer (because you'd be spilling out of the container) but there's no way to handle off-by-one errors inside a buffer (you meant to say "read from the beginning of the buffer up to the last character that's been read from the serial line" but you actually ended up including one more character because you wrote off + 1 instead of off).
It's still up to the programmer to manage these things, and sloppy programmers will still write sloppy code that does this sloppily.
However, and this is a very big and very important however, we're talking about a level where any kind of additional safety helps. Returning to the previous example, off-by-one access errors inside a statically-allocated buffer are generally easy to spot and get caught very quickly during QA (since ooh, let's look at the buffer is the first thing you say when you get a bug about repeatedly garbled data when reading data from the serial interface). Off-by-one access errors outside the buffer (accidentally zeroing all the TX buffer, oh, and the first byte of the structure next to it that's used by an entirely different module) are the icky ones that sometimes fly under the QA radar for months.
I don't think the safety gains are on the level that the Rust fandom expects, but no one in their right mind is in a position to not welcome them. No, Rust won't solve everything, and bad programmers will continue to productively shoot themselves in the foot in Rust, too, just as they can productively shoot themselves in the foot in pseudocode. But for people who realize that there's a pretty big chance their next bug is going to get somebody killed, anything that reduces the bug population is a welcome addition.
tl;dr In most embedded scenarios, this will help less than the Rust community wants to think, but enough that sane people welcome it!
- kazagistar 10y agoI'm not entirely familiar with embedded programming, but I don't think I understand your buffer example. A serial line adds to the buffer, incrementing an internal "current end position". You grab a drain iterator or a slice, incrementing the internal "current start position". At no point is there any manual arithmetic for any off by 1 errors.
- notalaser 10y agoThe opportunity for off-by-one errors is at the other end of the pipe, when you grab data from that same buffer and munch it in the application. Depending on how unlucky you are, that ends up involving crap like CRs and LFs and totally sane protocols that include the header length in the byte count for some packets, but not for other packets. It's probably not the best of examples, but the mistakes pop up the same way as for dynamically-allocated buffers -- you pick the wrong offset or count up to the wrong limit, and it doesn't make much of a difference if the buffer was statically allocated or not.