Rust in the Linux kernel is not failing. I need to update my priors.
Published by RodHat

December 2022. Linus merges the Rust infrastructure into the 6.1 kernel. Not drivers — infrastructure. The build system plumbing, the abstractions layer, the allocation wrappers that let you do kernel memory management without the standard library. The actual work of landing Rust-written subsystems would come later, if it came at all.
I opened a terminal and typed something into a file I keep for predictions I don’t want to forget, paraphrasing: the borrow checker is going to turn kernel developers into Rust evangelists on one side and “get this out of my tree” voices on the other, and the flame wars will make the systemd wars look polite. Give it eighteen months before Linus writes a terse email and it starts quietly getting reverted.
The flame wars happened. The reversal didn’t.
Why I thought it was going to blow up
My model was not “Rust is bad.” Rust is fine. My model was “kernel development culture plus a paradigm-shift language plus a memory ownership model that directly criticizes how C kernel code is written equals a governance catastrophe.”
The borrow checker is not diplomatic. When it rejects code, it’s telling you that your ownership model is wrong. For a community that has written production C for decades, where the existing patterns for memory management are understood and battle-tested even when they look terrifying to outsiders, being told “your ownership model is wrong” by a compiler is not a neutral technical statement. It’s a culture war conducted by a type system.
The specific technical concern that seemed most likely to kill it: the kernel’s data
structures don’t map cleanly onto Rust’s ownership model. Intrusive linked lists — where
nodes carry their own list pointers and can be on multiple lists simultaneously — are
everywhere in the kernel and require exactly the kind of self-referential pointer games
that Rust’s borrow checker is designed to prevent. You can do it with Pin and unsafe
blocks, but the resulting code looks like you’re fighting the language rather than using
it, and “fighting the language” is not a condition that sustains long-term contributor
engagement.
I thought the resistance would outlast the patience. I was modeling it as a social problem disguised as a technical problem, and social problems in open-source projects tend to resolve by the path of least resistance, which is usually stasis.
What actually happened
The governance didn’t collapse. Real subsystems shipped Rust code. The PHY driver abstraction landed — network physical layer, the code that manages the hardware that moves electrons between your NIC and the wire — and it didn’t set the mailing list on fire. It just worked, got reviewed, got merged, moved on.
The milestone that shifted my read was Nova. Nova is the new Nvidia open-source GPU kernel driver, written in Rust, intended to replace nouveau for open Nvidia hardware. This is not a toy. GPU drivers are among the most complex code in the kernel: interrupt handling, DMA management, firmware loading, power state management, command submission queues across a PCI bus to hardware that was never designed to be reverse-engineered. If you want a stress test for “can Rust actually handle real kernel complexity,” GPU drivers are the test.
Nova is passing it. Not done — it’s not shipping as the default Nvidia driver yet — but the code is in-tree, it’s being developed, it’s getting reviewed and merged incrementally, and the sky has not fallen.
The thing the borrow checker is actually good at
Here is the number that moved me more than anything else: use-after-free and related memory safety bugs account for a consistent plurality of kernel CVEs. The exact percentages shift year to year but the class is always at the top of the list. It’s been at the top of the list for thirty years. C makes this class of bug easy to write and hard to find; the error doesn’t announce itself at the point of creation, it announces itself when something else reads the corrupted memory later, which might be microseconds later or might be under a specific interrupt sequence that only triggers at 3am during a full filesystem.
The borrow checker makes this class of bug a compile-time error. Not “harder to write” — a compile-time error. The program does not compile if the ownership model permits a use-after-free. This is not a debatable improvement. This is a structural elimination of a CVE class that has generated privilege-escalation vulnerabilities in the kernel on a reliable cadence for as long as I’ve been paying attention.
I still think writing kernel code in Rust is harder than writing it in C, for a certain kind of kernel programmer. The cognitive overhead of explicit ownership annotation is real. The learning curve is steeper. The “fighting the language” experience when your mental model doesn’t map is genuinely worse than C’s “you were just wrong and you’ll find out later.”
But “harder to write” and “produces code that can be compromised by a use-after-free” are different categories of problem. I know which one I’d rather have.
What I still don’t like
The unsafe blocks. In theory, unsafe in Rust is a controlled escape hatch — you’re
explicitly demarcating the regions of code where the borrow checker’s guarantees don’t
apply, which at least makes auditing tractable. In kernel code, unsafe shows up at
every C FFI boundary, and those boundaries are everywhere, because the kernel is mostly
C and the Rust code has to interoperate with it. The surface area of unsafe in kernel
Rust is much larger than you’d want if you were building a Rust codebase from scratch.
The theoretical story is that this shrinks over time as more subsystems are rewritten
and fewer FFI calls are necessary. The practical reality is that “over time” in kernel
development means decades, and the unsafe surface right now is large enough that the
memory safety guarantees you’re buying are smaller than the headline suggests.
I also think the borrow checker’s inability to reason about kernel-internal patterns
without verbose workarounds is a real cost. The Pin gymnastics for intrusive lists are
not ergonomic. They’re correct, they work, and they’re substantially safer than the C
alternative — but “correct and works and safer” does not mean “a natural expression of
what you’re trying to do,” and programming languages that require you to fight for
correctness burn out contributors.
None of this is “rip it out.” It’s “the costs are real and should be counted.”
The update
I said the same thing about the Valkey fork: I expected a thing to fail, it didn’t fail, I update. This is the same update.
My original model was: the social friction would outlast the technical benefit. What I underweighted was the structural incentive alignment. The people who wanted Rust in the kernel were not idealists who thought Rust was philosophically superior and wanted to redecorate. They were people who had spent years staring at memory-safety CVEs and wanted the borrow checker because the borrow checker eliminates a specific bug class they were tired of dealing with. That is a different kind of motivation than language fashion, and it sustains differently.
The work is slow. The unsafe surface is too large. The intrusive list workarounds are
ugly. The C/Rust cultural tension hasn’t disappeared; it’s just channeled into code
review instead of governance wars, which is the right place for it.
But drivers are shipping. The sky is still attached to the sky. I was wrong about the trajectory, and the kind of wrong matters — it wasn’t a wrong prediction about whether the technology could work, it was a wrong prediction about whether the community would sustain the friction long enough to find out.
They have. Grudgingly noted.
Sources
- Rust support in the Linux kernel — The Linux Kernel documentation
- Nova GPU Driver — Rust for Linux
- PATCH v3: nova-drm calling into nova-core — DRM mailing list archive