By Design ■ Episode 3
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.
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 garbage, but by the GC scanning live data. 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.
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.
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.
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.
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. No hidden throws. No
catch blocks that swallow errors silently. No
"everything is fine" whilst the database connection has been
dead for forty minutes. Quite the novelty.
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
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. One does rather miss problems one never has.
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 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.
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 because it cannot prove the code is correct, 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 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 Proof
The Linux kernel accepted Rust as a supported language in December 2025. 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. Cloudflare built Infire, a custom LLM inference engine, in Rust, achieving 7 per cent 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.
45 per cent 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 per cent admiration rate. 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.
Episode 1: Hipp sacrificed write concurrency and gained the most deployed database in history. Episode 2: CSV sacrificed everything and gained universality. Episode 3: Rust sacrificed null, exceptions, garbage collection, and inheritance, and gained a language that the Linux kernel, the Windows kernel, and every major cloud provider now trusts with their infrastructure.
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.
No null. No exceptions. No garbage collector. No inheritance. No implicit mutability. Every "no" eliminates a category of bugs. 70% of C/C++ security vulnerabilities are memory safety bugs. Linux kernel: Rust accepted. Microsoft: 188K lines rewritten. Discord: ms to us. Nine years most admired. The borrow checker is not a barrier. It is a boundary.