Table of Contents
Why C Still Rules the Kernel (Even After Rust Arrived)#
For more than 50 years, one programming language has quietly powered the most important software in the world: operating systems, embedded devices, databases, game engines — and the Linux kernel itself. That language is C.
In recent years a challenger arrived. Rust promises what C has struggled to guarantee for decades: memory safety, no garbage collector, and high performance — a safer way to write low-level software.
Which raises an awkward question. If Rust was designed to solve C's biggest problems, why is the Linux kernel still mostly written in C?
First, what is the kernel actually doing?#
Before comparing the two languages, we need to know what sits underneath them. The kernel is the core of an operating system: the layer between applications and hardware.
When you open a file, create a process, allocate memory, or send data through a network, your application is not talking to the hardware. It is asking the kernel to do it. The kernel manages the most important resources a computer has — CPU time, memory, storage, and every attached device.
And because the kernel controls everything, the language used to build it must provide two things at once: extreme control, and extreme reliability.
Why C became the language of the kernel#
The first reason is simple: C is extremely close to the hardware. When C code touches memory, there is almost nothing between the source you wrote and the machine that executes it:
// Talking straight to the hardware: a memory-mapped device register
#define STATUS_REG ((volatile uint32_t *)0xFE201000)
*STATUS_REG = 0x1; // no runtime, no safety net — just your code and the machine
That closeness is exactly what an operating system needs. Kernel developers can control memory layouts, talk to devices directly, write drivers, and optimize every last detail.
The second reason is portability. C does not require a large runtime environment. The same language runs on a microcontroller with a few kilobytes of RAM and on a server serving millions of users. From tiny embedded boards to the world's largest data centers, C is everywhere.
But the biggest reason is history. The Linux kernel was created in C. Millions of lines of kernel code, drivers, and tools were built around it, and the entire ecosystem grew up together with the language.
The problem with C#
However, the same power that makes C useful also makes it dangerous. C gives programmers enormous freedom, and freedom comes with responsibility — the compiler does not always protect you.
Picture this: you write a pointer that references a chunk of memory that has already been freed.
char *data = malloc(64);
free(data); // the memory goes back to the allocator
data[0] = 'X'; // use-after-free — compiles silently, undefined behavior
In C, the compiler stays silent. It trusts you. The code compiles, passes your quick tests, ships to production — and then, on some random midnight, the server crashes without a trace. That is C's infamous undefined behavior: a buffer that overwrites nearby memory, a pointer into invalid data, memory that was released but is still being used.
These are not exotic bugs. A single dangling pointer inside a 30-million-line kernel can produce a CVE that haunts the internet for a decade. The problem is not that C is a bad language — it is that modern software has become too complex for humans to handle every edge case perfectly, every time.
Why Rust appeared#
This is exactly where Rust enters the story, with a different philosophy. Instead of trusting programmers to manage memory perfectly, Rust moves many checks into the compiler. The compiler analyzes your code before the program ever runs and refuses to build the classic disasters: use-after-free, invalid memory access, data races, ownership mistakes.
The same bug from the C example simply does not compile:
let data = vec![0u8; 64];
drop(data); // ownership of the buffer is explicitly released
data[0] = b'X'; // error[E0382]: borrow of moved value: `data`
// the build fails before the bug ever exists
The difference is a change in who gets asked the question. In C, the programmer says: "I know what I am doing." In Rust, the compiler asks: "Can you prove that this is safe?"
That single shift makes Rust dramatically safer for large, complex systems — which is precisely what a kernel is.
So why not rewrite Linux in Rust?#
If Rust solves so many of C's problems, why not rewrite the entire kernel? Because replacing C is not a programming problem. It is an engineering problem.
The Linux kernel has decades of development behind it. It supports thousands of different hardware devices. Drivers, tools, libraries, developers, and documentation all connect to the existing ecosystem, and rewriting millions of lines of working code would introduce enormous risk in exchange for no new features.
There is a deeper, more technical reason too. Rust does not live in a vacuum. Inside the kernel, it must constantly talk to legacy C APIs, and those interactions cross an unsafe boundary — the bridge between the two languages:
// Every line of kernel Rust eventually crosses this bridge.
// Inside `unsafe`, the compiler checks nothing — every safety promise
// the surrounding code makes is only as strong as this glue.
let ret = unsafe { bindings::register_chrdev(major, name.as_char_ptr(), &fops) };
if ret != 0 {
return Err(Error::from_errno(ret));
}
If that glue code contains a single bug, Rust's entire safety guarantee evaporates instantly. Maintaining correct, performant FFI layers between C and Rust is itself a massive engineering burden.
And even Rust cannot completely avoid unsafe operations. Writing an operating system means direct access to hardware, raw memory, and CPU state. Rust provides safer tools, but the kernel still needs raw power and flexibility.
The future is C and Rust, not C versus Rust#
The future probably will not be C versus Rust. It will be C and Rust working together — and that collaboration is already happening.
Since Linux 6.1, released in late 2022, Rust has been an official part of the mainline kernel. Today, production-grade Rust code runs inside Android's Binder driver and Apple's M-series GPU drivers.
The split of responsibilities is natural. C remains excellent when maximum control is required. Rust is excellent when safety and maintainability are priorities. Instead of replacing C overnight, Rust is becoming another powerful tool in the systems programmer's toolbox: new kernel components can be written in Rust, while the existing C code keeps running.
The scalpel and the robot#
C has survived for more than 50 years because it solved one of the hardest problems in computer science: how do we control machines efficiently? Rust exists because computers have become too complex to trust everything to human memory management.
C gives you a scalpel — razor-sharp, elegant, but it all depends on your hand. Rust gives you an intelligent robotic surgical system — constrained, precise, and loaded with guardrails.
In the operating system kernel, a domain where a 0.01-millimeter deviation means a system-wide crash, C is the veteran master surgeon and Rust is the next-generation surgical robot. You would not replace the surgeon with the robot. But you would not let the master surgeon operate without that intelligent precision either.
The future of systems programming is not about choosing between C and Rust. It is about knowing when to hand the scalpel to the master — and when to let the robot do the heavy lifting.
