Rust applicability to small embedded codebase - getting discouraged

OK, so here's the thing.

  1. Rust is definitely a more sophisticated language than C (and more principled than C++) is, but it still gives you all the low-level control if you ask for it. So you can pretend you are in C, and continue using unsafe, static mut, raw pointers, and uninitialized memory left and right, whether or not it is warranted. People will frown upon you if you do that, but it is possible, and there is a very straightforward if not trivial translation from C to unsafe Rust.

    Having written compilers before, I would risk saying that mechanical translation of C to non-idiomatic unsafe Rust wouldn't even need full type checking. IIRC it could be done in a purely syntactic way (except for the fact that you need at least partial type checking in order to even parse C).

  2. Of course, basically everything in that code would (have to) be unsafe, because the many unsafeties of C is exactly what Rust is trying to prevent. What you are observing here is that Rust is not C. Rust is not C++, or Java, or Python, or JavaScript, or C#, or Haskell. Rust is Rust. And trying to write any other language in Rust, while definitely possible, won't ever be pretty.

  3. What you have to understand, though, is that we, humans, don't mean "mechanical translation to an ugly subset" when we talk about transitioning a code base from one language to another. (Maybe some of us do, and that's for the worse.) The point of using a different language is not to change the syntax of pointers from int *p to p: *mut int or to change the name of 32-bit unsgined ints from uint32_t to u32. To a first approximation, nobody cares about that (again, important exceptions apply, since C's syntax is context-dependent, so it's notoriously harder to parse than Rust's).

    Instead, the point of using a different language is, or should be, to harvest the benefits of that language and ecosystem. This should entail embracing the idioms of the new ecosystem, rather than trying to force other idioms or just downright outdated practice onto the new codebase. What you are essentially saying is: "Doctor, I'm writing C in Rust, and it hurts." To which the doctor will reply: "Then don't write C in Rust, and it won't hurt!"

  4. I will go one step further and say that the aforementioned practices around global mutable state are not considered decent even in C. Unfortunately, the embedded programming world has a tendency of declaring that "it works", and people then think that they can stop caring about good practice altogether, and just write the shortest, ugliest code that will happen to generate the machine code they think is (half-)correct. This, however, is not an intrinsic necessity even in embedded or low-resource systems.

    I have written my own hardware abstraction layer before, exactly because I was dissatisfied with the quality of some other HALs. I quickly ripgrepped the code base, and I found only 5 statics in the entire thing. Taking a closer look, only a single one of them is necessary (because it's the interrupt handler vector, which has to be global state, given that interrupts can't receive parameters on the AVR), and the rest of them would be trivial to move to a struct and be replaced with properly scoped locals.

    Apart from marginal writing convenience, there is no real benefit in extensively relying on mutable global state. On the contrary, it's harmful in the long run, because it hurts maintainability, and causes bugs to sneak in over the years. That is simply the nature of shared global state. The fact that Rust points out that global mutable state is highly unsafe is not Rust's fault – it's a feature, not a bug. Global mutable state being dangerous is solely the fault of global mutable state itself, and instead of basically complaining that Rust doesn't let you shoot yourself in the foot, you should look at and fix the problems it is trying to point out.

21 Likes