Rusty rewrites
Preface
I’m sure by now you’ve heard a billion different C++ vs. Rust arguments, and I really am not going to be making one here. As somebody who actively develops in C++ and Rust, I often get dragged into debates surrounding the two languages and their ecosystems.
This isn’t going to be an exhaustive look at the whole picture, and it certainly isn’t going to be another “Which one should replace the other” kind of post. Instead, I want to talk a bit about an all too common pattern of unnecessary rewrites.
C++, you bastard
Being frank, the more I learn about the language, and the more I develop with it, The more I realize it’s often just “okay”. Forget Rust, there’s a long list of languages that come with easy-to-use build systems out of the box, provide modern and feature-rich standard libraries, far less foot guns, and don’t force me to tip toe around performance / safety traps so we can work with code from ‘97. It’s a language obsessed with backwards compatibility and ABI stability, and that’s really it.
Yes, I understand the performance argument. It’s something I wrestle with daily in an embedded context. But most systems do not need to be hyperfocused on micro-optimizations to squeeze out every last ounce of performance. There’s some serious arguments to be made for struggles with creating performant C++ code while matching modern features and safety contexts that other languages do much better. I think ABI stability, backwards compatibility, and being a superset of C are C++‘s greatest strengths.
That can either be a massive pain in the ass or a huge timesaver. Sure, it’s got a laundry list of flaws, but that backwards compatibility and ABI stability is something people who don’t work with legacy systems often sleep on. In my computer engineering role, I largely develop with C/C++, and it’s a great match. We can interface with some legacy (but quite necessary) embedded devices, and integrate their software into our newer C++ systems, and go about our business. Plus there are so many vendors and existing infrastructure built around C++ that it’s really hard to avoid in certain industries. And despite my hopes for something like embedded Rust, I really don’t see C or C++ being knocked off the embedded pedestal any time soon.
Depending on who you ask, it sounds like I’m shitting on C++ a bit, but what am I really saying here? C++ remains indispensable in certain domains, particularly where ABI stability, hardware proximity, and long-lived ecosystems matter. But the idea of it being a default choice for new systems is steadily weakening as viable alternatives mature. More and more languages are increasingly capable of meeting many of the same performance goals while offering stronger safety guarantees and more modern tooling.
So what’s the deal with Rust?
Rust isn’t perfect, but I think it does a damn good job of filling in the holes (forcing a large category of safety issues / stability bugs to be addressed at compile time, a strong out-the-box build system, an ownership / borrowing system that lends itself well to systems-level architectures, list goes on, blah blah blah).
Because of is strengths outshining its flaws, I find an increasing number of developers (including myself), considering it as an alternative to a C++ default.
An ever growing problem
So C++ is starting to make less sense in certain contexts, and Rust is a great fit in a lot of cases. What’s my issue? The cases people are choosing.
I think that people often look at already functional systems and suggest rewrites to insert features and benefits that the already existing architecture lacks. While this is not exclusive to C++ / Rust by any means, it is a growing pain in my ass. The question that so many people fail to continue asking is “why?”. Sure, they’ll first answer it with the generic benefits of a language like Rust. But did the underlying system really need those benefits? Would all the work to rewrite it with a completely different programming paradigm and structure be worth it? Honestly, only in some very specific cases! I’m all for Rust, but if your rewrite in Rust has you relying heavily on unsafe blocks and raw pointers to mimic what C++ what already doing, then what was the point of that? Better safety guarantees act as one of Rust’s biggest benefits, but unsafe is a land that can easily throw out all of it.
Rust doesn’t negate logical errors either. Take an already existing legacy system
Learning the shape of a layer
When I start working with a new system, I deliberately poke at the abstraction boundary. I write queries that should be slow and verify they are. I hit rate limits intentionally. I let a cache expire and watch what happens downstream.
This sounds paranoid. It’s just calibration. The goal isn’t to distrust the abstraction — it’s to build a mental model of the thing it’s wrapping, so that when it leaks (when, not if), you have somewhere to look.
The good news
The flip side of “abstractions always leak” is: the leaks are informative. When your abstraction breaks, it’s often pointing directly at a concept you hadn’t named yet. Something that wanted to be explicit has forced its way into visibility.
Some of the cleanest designs I’ve seen came from someone chasing a leak far enough to find the missing concept underneath, naming it, and building it into the abstraction properly.
The leak is the system telling you what it needs.