Memory Safety Is More Than a Language Choice

In the world of software development, it’s tempting to believe that switching programming languages can solve deep-seated issues within a project. Whether it’s moving to a language that promises better memory safety or enhanced security, companies often see language changes as a quick fix. Recently, Rust has gained traction for its strong guarantees around memory safety and type checking, with many advocating for it as a replacement for established languages like C and C++.

However, the belief that simply switching languages can solve all problems is an oversimplification. While Rust’s borrow checker effectively addresses certain memory safety vulnerabilities, it tackles only 7 out of the 25 most dangerous software weaknesses listed in the 2022 CWE Top 25 [1]. The remaining issues often stem from application level challenges, not deficiencies in the language itself. This highlights a reality: switching languages is rarely the panacea it’s made out to be. The success of any project relies on how well teams apply their knowledge, work together, and manage complexity.

A Real-World Example: Challenges Beyond the Technology

Several years ago, I was called in by an organisation in Europe to help assess a program that was struggling to meet its objectives. The development team had chosen Java over C++, believing that Java’s features, such as automatic garbage collection, would lead to fewer bugs and a more reliable system. At the time, real-time Java was considered a promising technology, and many in the industry believed it would displace C++ for certain use cases. However, as the project encountered difficulties, technical management began to question whether the choice of Java, specifically its compiler and runtime, was the source of the issues. One of the concerns raised was about the real-time garbage collector the team was using. It was part of a commercial off-the-shelf product, built specifically for use in soft real-time systems. After conducting a thorough analysis of the codebase and assessing the system’s performance, it became clear that the problem was not the Java runtime or the garbage collector. In fact, the real-time garbage collector was performing exactly as intended. It was perfectly suited to the demands of soft real-time, mission-critical environments. The real challenge, as it turned out, was not with the language or the technology but with the complexity of the project itself. The system had grown organically with an “evolving architecture”. The team’s ongoing efforts to patch bugs without addressing the underlying architectural issues were creating new problems with each fix. It was a classic case of a project becoming difficult to manage due to its scale and the lack of a cohesive, long-term architecture. This shows an important point: even with the right tools such as a high quality real-time garbage collector, problems can still crop up because large systems are naturally complicated. People often look for a technological scapegoat, but the real solution is usually found in better processes, strong engineering led teams, and solid architecture, not just swapping out tools or languages.

Rust: A Powerful Tool, But Not a Cure All
The rise of Rust in recent years has led many to suggest it as a solution to the memory safety issues inherent in languages like C and C++. Rust’s borrow checker and type system certainly address important vulnerabilities, such as buffer overflows and use-after-free errors. However, as noted earlier, these protections only mitigate some of the issues that software teams face. In reality, many challenges come from effective requirements and systems analysis, design decisions, architecture (or lack of), and application logic, which no language can fix. While Rust is certainly a strong choice for new projects, the idea that it or any other language can solve all problems is mistaken. In the case of the Java project I mentioned, Rust wouldn’t have solved the many challenges the team was facing.

Dealing with Legacy Code in C and C++
For industries like defence, medical devices, and IoT, where billions of lines of legacy C and C++ code are in active use, the answer usually is not to discard everything and rewrite it in the latest language. Companies have made enormous investments in their existing codebases, and a full-scale migration is often costly, risky, and impractical. An alternative is a strategy to refactor and improve the existing codebase in a way that minimises disruption and risk. Companies must learn how to assess their legacy systems, identify critical weaknesses, and apply targeted memory safety improvements. Techniques like static analysis, manual refactoring, and introducing modern memory-safe practices can go a long way in securing these systems without resorting to a wholesale rewrite. In industries like defence, medical, and IoT, where reliability, security, and safety are paramount, this approach may be more practical than a complete rewrite. For the likely billions of lines of C and C++ code that companies are currently working with, the key lies in taking a measured approach.

Control the Toolset
The lesson is clear: no programming language, whether it’s Rust, Java, or C++, can replace solid engineering practices and team collaboration. While modern languages offer valuable features that prevent certain classes of errors, project success ultimately depends on teams having the engineering expertise and language skills to use their tools effectively. Switching to a new language might quickly solve some problems, but it will also introduce new challenges. The key to success is mastering the toolset you’ve chosen, developing best practices, and ensuring that teams have the support they need to maintain control over architecture and code quality.

Final Thoughts

For industries like defence, medical devices, and IoT, where reliability, security, and compliance are paramount, the language choice is just one piece of the puzzle. Rather than focusing solely on switching languages, companies can invest in improving the expertise of their teams, developing strategies for refactoring legacy code, and applying disciplined engineering practices to ensure that their software is safe, secure, and reliable. In the end, success is less about the language you choose and more about how well it is applied

[1] Joe Sible and David Svoboda, Rust Software Security: A Current State Assessment, SEI Blog, Carnegie Mellon University Software Engineering Institute, December 12, 2022. [Online]. Available: https://insights.sei.cmu.edu/blog/rust-software-security-a-current-stateassessment/