I am really impressed by the Google Sanitizers. These tools are basically like a compiler-built-in variation of what Valgrind does. Using Rust doesn't obliviate the need for these tools, but I could never trust any substantial project written in C or C++ without them.
The very existence of these tools is proof that writing safe code in C or C++ is extraordinarily difficult. A code base shared by a team of developers increases the difficulty exponentially for each team member added. In my experience, this is because understanding the safety implications of wild void *
pointers, bounds checking, avoiding double-frees, and correctly synchronizing shared access with mutual exclusion locks requires keeping the entire program state in your head at all times.
It isn't always possible to review code written by other team members, and even when you do, it isn't always possible to keep the state of the existing code in your head, while also bringing in the new state from the new code in a cohesive way, without introducing new errors. In other words, the compiler only does what you tell it to do, and there are many many more unsafe things you can tell it than there are safe things. Thus Valgrind and the Sanitizers were born.
What about Rust? If you use any unsafe
, these tools will help check (at runtime) what the Rust compiler cannot (at compile-time). So these tools are still a good fit. However, if you are only using the safe subset of Rust, it's very unlikely that these tools will tell you anything you don't already know. E.g. that the code is memory safe, thread safe, free of undefined behavior and data races.
BTW, the leak sanitizer, Valgrind's memcheck tool (for leak detection), and leak detectors in allocators like jemalloc are all still very relevant to the safe subset of Rust. The language doesn't provide protection against memory leaks: Memory Leaks are Memory Safe
The incredible ease by which C and C++ can violate memory safety has bitten me more times than I can count. Often with a rude 3 AM wake up call with segfaults in production code that customers depend upon for their businesses. This same ease of shooting oneself in the foot is the sole reason I hated preemptive threading for many years, to the point where I avoided it entirely and put all of my trust in process boundaries without shared memory. Not to mention countless weeks spent debugging code, saying things like "I have no idea why this doesn't work," only to find that the bug actually exists in some completely unrelated code path.
If you can write flawless C or C++ code (and that's a big if! In fact, I don't trust anyone with the hubris who believes they are so flawless) then maybe these languages are just fine for you. But only if you're the only person that ever touches the code. Alternatively if you already have an excellent CI story with a comprehensive test suite, including automation around Valgrind or Google Sanitizers, you're probably pretty well off at avoiding C++ pitfalls. You just happen to find out about them during potentially long CI test runs, instead of compile time in your development workspace.
I don't mean this as a harsh critique of C++. But it's rightfully deserved, so I don't feel too bad.