By Design — Episode 03
You have written this bug. You have returned null from a function because the real value was not available yet. You have caught an exception three levels up and logged "something went wrong" without knowing what. You have pushed to production, and at 3 AM the on-call phone rang because a pointer pointed to memory that no longer existed.
Every language you have ever used let you compile that code. Every single one said: "Looks fine to me."
In 2006, Graydon Hoare walked up twenty-one flights of stairs to his apartment in Vancouver because his elevator had crashed. The software, written in C, had a memory bug. The elevator ran on code that could access freed memory, dereference null pointers, and corrupt its own state. Hoare lived on the 21st floor. It was not the first time the elevator had crashed. It was, however, the last time he accepted it as normal.
He started writing a programming language that evening. He named it after the rust fungus: an organism, as MIT Technology Review later noted, "over-engineered for survival." Rather fitting.
The Complaint
"Rust is too hard. The borrow checker fights you. There is no garbage collector. No null. No exceptions. No inheritance. Everything is immutable by default. Why does this language say no to everything?"
One does hear this. Usually from someone whose last production outage was caused by one of those features.
The Design: No Garbage Collector
You know the garbage collector pause that spiked your latency? The one you tuned, and tuned, and tuned, and then it spiked again?
Memory in Rust is managed by ownership. Every value has exactly one owner. When the owner goes out of scope, the value is freed. No garbage collector runs in the background. No stop-the-world pauses. No GC tuning. No memory leaked because a reference was held somewhere you forgot about.
Discord documented this in a now-famous blog post. Their Read States service, written in Go, experienced latency spikes every two minutes. The Go garbage collector paused to scan the entire LRU cache. The spikes were not caused by a massive amount of garbage, but by the GC scanning live data to determine what was free. The cache was mostly alive. The scan was mostly pointless. The latency was entirely real.
They rewrote it in Rust. The latency dropped from milliseconds to microseconds. The spikes disappeared. Not because the Rust code was more clever. Because there was no garbage collector to pause. One does rather appreciate a solution that works by removing the problem.
The trade-off is real: you must think about ownership. Every value must have a clear owner. Every borrow must have a clear lifetime. The compiler enforces this at compile time. The cost is paid during development, when your IDE is open and your coffee is warm. The reward is paid in production, when neither is true.
The Design: No Null
You know the null check you forgot last Tuesday? The one that worked fine in testing because the test data always had a value?
In 2009, Tony Hoare (no relation to Graydon) stood before an audience at QCon London and called null his "billion dollar mistake." He had invented it in 1965 for ALGOL W, and he described it as the source of "innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years." One does note that the estimate was conservative even then.
Rust has no null. Instead, it has Option<T>: a value is either Some(value) or None. The compiler forces you to handle both cases. You cannot call a method on a value that might not exist without first checking whether it exists. The billion-dollar mistake is prevented by the type system, at compile time, with zero runtime cost.
This is not convenience. It is the elimination of a defect class. Every NullPointerException your Java code has ever thrown, every segfault from dereferencing null in C, every "undefined is not a function" in JavaScript: Rust's type system makes them structurally impossible. Not unlikely. Not caught by tests. Impossible.
The Design: No Exceptions
You know the try-catch that silently swallowed an error? The one where the catch block logged a message nobody read, and the application continued in an undefined state for forty minutes before someone noticed?
Functions that can fail in Rust return Result<T, E>: either Ok(value) or Err(error). The failure path is visible in the function's type signature. Every caller knows that the function can fail. Every caller must handle the failure or explicitly propagate it with the ? operator.
There are no hidden throws. No catch blocks that swallow errors silently. No stack unwinding that crosses function boundaries invisibly. No "everything is fine" until a try-catch three levels up catches something it does not understand and logs "An error occurred." One does wonder how many production incidents have begun with that exact log message.
The ? operator makes error propagation ergonomic:
fn read_config(path: &str) -> Result<Config, Error> {
let contents = fs::read_to_string(path)?;
let config: Config = toml::from_str(&contents)?;
Ok(config)
}
Each ? either unwraps the success or returns the error to the caller. The code reads linearly. The error handling is explicit. The types tell the truth. Quite the novelty.
The Design: No Inheritance
You know the six-level class hierarchy where nobody remembers what the grandparent overrides? The one where changing the parent class broke seventeen subclasses in ways that only appeared in integration testing?
Rust has no class inheritance. No extends. No class Dog extends Animal. No hierarchy where the behaviour of your object depends on decisions made four levels above by someone who left the company in 2019.
Instead, Rust uses traits: shared behaviour defined as interfaces, implemented by types. A struct can implement as many traits as it needs. Traits compose. They do not cascade.
There is no diamond problem. No fragile base class. No "I changed the parent class and now everything behaves differently." The relationship between types is explicit: this type implements this behaviour. Full stop. One does rather miss problems one never has.
This is composition over inheritance, enforced by the language. Not a design pattern you choose to follow when you remember. A constraint the compiler imposes whether you remember or not.
The Design: No Implicit Mutability
In Rust, all variables are immutable by default. To make something mutable, you write let mut. The act of changing state becomes a conscious, visible decision in the code. Not a default. A declaration.
Combined with the borrow checker's rule that you cannot have a mutable reference and an immutable reference to the same data at the same time, this eliminates data races at compile time. Not by detecting them at runtime. Not by crashing when they occur. By making them structurally impossible to express.
Every concurrent bug you have ever debugged, every "it works on my machine" that vanished under load, every race condition that appeared once in ten thousand runs: Rust's type system prevents them by refusing to compile code that could produce them. The compiler does not trust you. It is, one must concede, entirely justified.
The Trade-Off
Let us be honest. The learning curve is real and well-documented.
The borrow checker will reject code that compiles without complaint in every other language you know. It will reject code that is, in fact, correct. It will reject code that has no bug. It will reject code because it cannot prove the code has no bug, and Rust has decided that "cannot prove safe" is the same as "unsafe."
This is frustrating. It feels adversarial. It feels like the compiler is wrong and you are right and the code is fine and why will it not just compile.
It is not wrong. It is conservative. And in the gap between "probably correct" and "provably correct" lie the bugs you would have shipped to production, discovered at 3 AM, and spent three days debugging whilst questioning your career choices.
The Rust community is honest about this cost. The first three months are painful. The compiler's error messages are unusually good (it tells you what went wrong and often suggests the fix), but the frequency of those messages during learning is high. You will argue with the compiler. You will lose. You will, eventually, realise that losing to the compiler is considerably cheaper than losing to production.
The reward: once it compiles, it works. Not always. But with a frequency and reliability that other systems languages do not match. The category of bugs that Rust eliminates (use-after-free, null dereference, data races, buffer overflows) accounts for approximately 70% of all security vulnerabilities in C and C++ codebases, according to research from Microsoft and Google's Project Zero. Seventy percent. One does find that number rather difficult to ignore.
The Proof
The Linux kernel accepted Rust as a supported language in December 2025. It is no longer experimental. Dave Airlie, maintainer of the DRM subsystem, stated that the DRM project was approximately one year away from requiring Rust and disallowing C for new drivers. The kernel contains 34 million lines of C and 25 thousand lines of Rust. The transition has begun. One does note the ratio with a certain quiet patience.
Microsoft has rewritten 188,000 lines of Windows kernel and DirectWrite code in Rust, with a stated ambition to eliminate C and C++ from its entire codebase by 2030. When Microsoft, a company not traditionally associated with radical architectural courage, decides to rewrite its kernel in a new language, one does pay attention.
Discord's Go-to-Rust migration eliminated garbage collector latency spikes and reduced response time from milliseconds to microseconds. The blog post has become required reading in systems engineering circles. Quite deservedly.
Cloudflare built Infire, a custom LLM inference engine, in Rust, achieving 7% faster inference than vLLM. AWS, Google, and Meta all run Rust in production at significant scale.
Android 16 ships with Rust-built components in the kernel (ashmem memory allocator). Millions of devices run Rust in production without their owners knowing or caring. Which is, of course, exactly how infrastructure should work.
45% of enterprises now run Rust in non-trivial production workloads. The Stack Overflow Developer Survey has named Rust the most admired language for nine consecutive years, with an 83% admiration rate in 2024. Not because it is easy. Not because the learning curve is gentle. Because the elevator stops crashing.
The Principle
Every feature a language grants is a failure mode it accepts. Every convenience added is a bug category normalised. Every "yes" comes with a cost that compounds over decades, paid not by the language designer but by every developer who inherits the codebase.
The courage to say no is the rarest engineering virtue. Rust proves that a language can refuse convenience, refuse familiar patterns, refuse the features that every other language considers mandatory, and win adoption not despite the refusal but because of it.
The borrow checker is not a barrier. It is a boundary. And boundaries, applied with discipline, are what separate systems that survive from systems that merely ship.
Graydon Hoare's elevator still works. One rather suspects it is no longer written in C.
Graydon Hoare stepped down from Rust in 2013. The language he started in frustration on a stairwell is now in the Linux kernel, the Windows kernel, and the infrastructure of every major cloud provider. The fungus, it turns out, was indeed over-engineered for survival.
Read the full article on vivianvoss.net →
By Vivian Voss — System Architect & Software Developer. Follow me on LinkedIn for daily technical writing.