indraj.net v3

HSTS enabled

Rust is a waste of everyone's time

While many programming languages are useful, most are unremarkable in the sense that they do not generate press headlines or attract attention from government agencies. I think the majority of people would agree that this is the normal state of affairs.

However, in recent years, a new programming language known as Rust has been gaining widespread and frankly undeserved publicity, not based on the merits of any innovation in its design or syntax, but rather due to a bizarre static analysis feature of the compiler and the emission of instructions which check for (and rather inelegantly handle) out-of-bounds array accesses at runtime.

In all other respects, it is no different to the median vision of a new C++ if backwards compatibility with C and object-oriented style are not considered to be core goals. Rust inherits misfeatures such as hidden control flow (i.e. RAII) and the tendency to expand with absolutely no regard for orthogonality, conceptual integrity or even the capacity of the human brain. To put it another way, what passes for language “evolution” today will become tomorrow's “technical debt”.

Furthermore, it has several unique misfeatures of its own. Let's start with the concept of “borrowing”, which Rust advocates often praise as its crowning achievement. What this slightly misleading term essentially boils down to is:

  1. There can only be one mutable reference to a location in memory in a given lexical scope (sort of); or
  2. There can be several immutable references to the same location in memory; and
  3. The use of mutexes prior to dereferencing is enforced by the compiler.
The practical upshot of rules 1 & 2 is that it is often necessary to restructure code in a way that is not intuitive to write or to read. Rule 3 is OK.

Why does this matter?

It matters because the “borrow checker” has far less utility when combined with memory arenas, a concept that all experienced programmers will be familiar with. Memory arenas are essentially large chunks of memory which can have multiple references from different offsets. This eliminates the overhead of memory allocation and de-allocation for individual variables (RAII) and greatly reduces the risk of of both memory leaks and use-after-free bugs.

What about security?

Contrary to popular perception, it is not the “borrow checker” that is responsible for Rust's strong memory safety guarantees; rather, it is the presence of runtime array bounds checking instructions in compiled binaries. There is absolutely nothing remarkable or unique about this, and it is not difficult to imagine the inclusion of this feature in a new descendent of C. (If we consider Go to be part of this category, then we can say that this has already been realised.)

According to various statistics, between 65% and 81% of security vulnerabilities in major software products like Google Chrome, Android, Mozilla Firefox, Microsoft Windows and Apple macOS relate to memory corruption. Rust advocates often state that it is the pervasive use of “unsafe” programming languages like C and C++ that have led to such shoddy security.

I have a different theory – what these products all have in common is that their codebases are huge and terrible; full of crufty hacks accrued over decades by people who didn't know what they were doing and didn't care because the customer had already been conditioned to accept substandard software.

Memory corruption bugs are actually just mundane logic bugs with a special label designed to serve the more political purpose of advocating for “memory safety”. They are also the easiest type of bug to spot, which is why they are so well-represented in statistics.

Supply chain attacks

There is a lot more to security than just “memory safety”. For example, there is an observable tendency in the Rust community to think that downloading random libraries from crates.io without performing due diligence is OK. One benefit of using C on modern UNIX systems is that programs can be dynamically linked against libraries from the OS package manager. These libraries are usually subject to independent quality assurance processes, and programs linked against them benefit from security updates without recompilation, as ABI compatibility is guaranteed.

Very few Linux distributions shipped the XZ backdoor in their stable repositories, while Rust programmers using crates.io would probably not have been so fortunate. However, the fast development pace of the language leaves them with no credible alternative. As the number of dependencies goes up, this elevated risk increases exponentially. At the same time, reducing the number of dependencies is a difficult proposition to swallow for many Rust advocates, as it is the main reason why programmers feel more “productive” with Rust compared to other systems-level languages in spite of its awful design. Nevertheless, with increasing regulatory pressure in Europe and other regions of the world, they may eventually be left with little choice.

What can I do instead?

Those commonly-quoted statistics from earlier are only a half-truth. They are confirmed vulnerabilities, but exploitability is a completely different question. Modern compilers, modern operating systems and modern CPUs contain mitigations designed to make it extremely difficult for attackers to take advantage of these bugs. Since GCC 14, you can pass the convenient ‑fhardened flag to the compiler-driver and linker to enable many of these protections. OpenSSF also has a guide detailing additional hardening options.

Go forth and continue writing in C, and don't let the Rust mob scare you. Maybe something better will come along one day, but this isn't it.


Belfast, United Kingdom
Copyright © 2024-2025 Indraj Gandham
Licensed under CC BY-SA 4.0