C++ and Rust
Every couple of years, C and C++ are declared dead. Recently, Microsoft Azure CTO Mark Russinovich publicly stated that they should be deprecated, in favor of Rust. Though expressed in a personal capacity, it’s an interesting take for someone from Microsoft, which has an enormous C++ code base and many active members in the C++ committee.
Regardless, C and C++ are still very much alive and kicking – each the lingua franca of many industrial software development environments. Yet, Rust is the apparent ‘place to be’ these days in systems programming land. What is it about this language that makes it so attractive? Let’s look at it from a C++ perspective.
Notable differences
First off, Rust offers a clear and objective advantage over C++: guaranteed memory safety for the compiled result. Essentially, Rust trades off compilation time (and compiler complexity) for a safer runtime. The compiler will try to prove to itself that the code you feed it is memory safe, using its type system and sometimes the help of the developer to indicate things like variable or reference lifetime dependencies.
There have been multiple independent security surveys on large C and C++ code bases that have shown a consistent and whopping 70 percent of all bugs and security issues to be memory safety related. In light of this, trading compile time for a memory-safe runtime seems a no-brainer. Writing quality C++ using the right tools, tests, best practices and compiler sanitizers can get you there too, but it doesn’t provide hard guarantees from the get-go. The ‘guard rails’ Rust implements to protect you from shooting yourself in the foot may sometimes be complex or even irritating, but think about the considerable benefits a runtime memory safety guarantee brings to programming concurrent software.
Another notable difference to C++ is the way Rust integrates the handling of errors and function result values. Unlike C++, it has no exceptions; it offers mechanisms to deal with errors using regular control flow. Result values must be processed, forcing the developer to implement error handling and prevent incorrect code, so errors or exceptions rarely fly under the radar. If during runtime, things really go sideways – say memory is exhausted – Rust will generate a so-called panic, a fatal error, stopping the thread in question (which can be handled gracefully).
The Rust compiler, built on top of the LLVM compiler back-end, is evidently very pedantic, to uphold memory safety and correctness of code, but quite helpful at the same time. Consider it a pair programmer, looking over your shoulder and providing readable error messages, even suggesting potential solutions. Aside from the compiler, most of the Rust tooling ecosystem revolves around Cargo, which is a build system, a package and dependency manager and much more, all in one. Despite the wealth of package managers, build systems and other tools available for C++, setting up a serious C++ project can still be a pain in the rear end.
Being relatively young, Rust has had the luxury of assimilating 40+ years of language development into its syntax and structure. Much of the inspiration was drawn from both imperative languages like C++ and functional languages like Haskell and Scheme. This gives it the subjective benefit of a very ‘modern’ feel. Moreover, many things in Rust are expressions, providing more flexibility in code notation.
In my previous contribution, I argued that the user base of a programming language is of paramount importance. Rust, with help of the Rust Foundation, is gradually building this user base, as can be deduced from many indicators, like the Tiobe index and the Stack Overflow survey, and by the adoption of Rust in the Linux v6.1 kernel – not a minor feat. As more and more security reports claim net positive effects strongly related to the use of memory-safe languages like Rust, the user base will continue to grow. Every pointer (no pun intended) seems to indicate a bright future for Rust.
Not all sunshine and roses
Should we then just deprecate C and C++ in favor of Rust? In my opinion, this poses a false dichotomy. Why should we have to choose between one or the other? As software engineers, we should use the right tool for the job.
Migrating to Rust isn’t all sunshine and roses either. When you have a large body of existing C++ code, Rust isn’t directly going to be of help, unless you’re willing to drop to C APIs for interaction. There are tools for C++-level interoperability, but these are still in their infancy. Of course, you could also rewrite your code – but that isn’t going to be easy, especially not when you lean heavily on advanced templates. And when you’re relying on absolute maximum performance requirements, safe Rust may not be up to it (yet?). Furthermore, in stricter environments like automotive or aviation, standards often prescribe the use of a formally specified programming language, which Rust currently is not.
The best advice is to not pin yourself down on a single programming language. Learning multiple languages is generally really beneficial and will improve your competence, style and knowledge in every language you master. So take a look at C++, Rust and other alternatives to broaden your perspective – or just for the sheer fun of it.
In the new 4-day training course “C++ fundamentals,” organized by High Tech Institute in the last two weeks of March, Kris van Rens introduces participants to the language basics and essential best practices.