TL;DR
The Linux kernel is 36 million lines of C. Rust has been slowly entering the kernel since Linux 6.1, but progress is measured in individual drivers and abstractions – a few thousand lines per release cycle. What if you skipped the incremental approach and asked Claude to rewrite major subsystems wholesale? I spent a weekend scoping this as a serious project plan: breaking the kernel into translatable units, estimating token costs, identifying the hard walls, and testing Claude’s ability to produce correct Rust translations of real kernel C. The conclusion: Claude can translate isolated, well-bounded kernel modules with surprising accuracy. It cannot translate the kernel. The difference between those two statements is the entire lesson.
Why This Is Not as Stupid as It Sounds
Rust in the Linux kernel is already happening. Linus Torvalds merged initial Rust support in Linux 6.1 (December 2022). As of 2025, Rust drivers exist for Android Binder, Apple GPU, NVMe, 9P filesystem, and several network drivers. The Rust-for-Linux project has been building safe abstractions over the kernel’s C APIs for three years.
The bottleneck is not whether Rust belongs in the kernel. That debate is over. The bottleneck is developer hours. Every Rust abstraction requires a human who understands both the C kernel internals and Rust’s ownership model deeply enough to produce a correct, safe translation. There are perhaps a few hundred people on Earth with this combination of skills, and most of them have day jobs.
The question is whether an LLM can compress the translation timeline. Not by replacing those experts, but by producing first-draft translations that experts can review rather than write from scratch. That is the framing that makes this worth exploring instead of dismissing.
The Scope of the Problem
The Linux kernel (v6.12) is approximately:
| Component | Lines of C | Files |
|---|---|---|
| Drivers | ~21M | ~32,000 |
| Architecture-specific | ~4.5M | ~10,000 |
| Filesystems | ~2.1M | ~2,500 |
| Networking | ~1.8M | ~2,200 |
| Core kernel (sched, mm, ipc) | ~1.2M | ~800 |
| Sound | ~1.5M | ~3,000 |
| Security modules | ~300K | ~400 |
| Crypto | ~200K | ~350 |
| Everything else | ~3.4M | ~5,000 |
| Total | ~36M | ~56,000 |
Drivers alone are 58% of the codebase. Architecture-specific code is another 12%. The “core kernel” – the scheduler, memory manager, IPC, and process model – is only 3% of the total lines but contains 95% of the complexity.
The Translation Unit Problem
You cannot feed 36 million lines of C into Claude and get Rust back. Context windows do not work that way, and even if they did, the kernel is not a linear program – it is a deeply interconnected graph of subsystems with implicit contracts, undocumented invariants, and decades of accumulated behavior that the code relies on but does not state.
The first question is: what is the right translation unit?
Option 1: File by File
Translate each .c file to a .rs file individually. This is the obvious approach and it fails immediately. Kernel C files depend on dozens of headers, macros defined three directories up, inline functions in architecture-specific code, and global state shared across subsystems. A single file in isolation is missing most of its context.
I tested this by asking Claude to translate kernel/sched/fair.c (the Completely Fair Scheduler). The file is 12,000 lines. Claude produced syntactically valid Rust, but it invented abstractions for every kernel API it could not see (struct rq, struct task_struct, struct cfs_rq). The result compiled against placeholder types but had zero semantic correctness. It was a Rust-shaped skeleton with no bones.
Option 2: Subsystem by Subsystem
Translate an entire subsystem – the CFS scheduler, the ext4 filesystem, the TCP/IP stack – as a unit. Feed Claude the subsystem’s source files plus the header files it depends on.
This is better but hits the context window. The TCP/IP networking stack is ~200,000 lines across ~400 files. Even with Claude’s 200K token context window, you can fit roughly 50,000-70,000 lines of C (tokens are not characters, and C has a lot of structural overhead). A single subsystem often exceeds the window.
Option 3: Module by Module
The kernel’s module system provides natural boundaries. A loadable kernel module (LKM) has a defined entry point (module_init), exit point (module_exit), and interacts with the rest of the kernel through a documented API surface. Modules range from 200 lines (a simple char device) to 50,000 lines (a complex filesystem driver).
This is the right translation unit. Modules under ~5,000 lines fit comfortably in the context window with their header dependencies. The API surface is explicit. The behavior is testable in isolation.
I tested this with three real kernel modules of increasing complexity.
Test 1: A Simple Character Device Driver
The simplest possible kernel module: a /dev/null-style character device that accepts writes and discards them.
Source: ~150 lines of C Claude’s Rust output: ~180 lines of Rust Verdict: Correct
Claude produced a clean Rust translation using the kernel crate’s abstractions. It correctly mapped file_operations to Rust trait implementations, used Box<T> for heap allocations instead of kmalloc/kfree, and handled the __user pointer copies with UserSlicePtr. The ownership model naturally enforced the cleanup that the C version handled with explicit goto err chains.
This was encouraging but unsurprising. Character device drivers are the “hello world” of kernel programming, and the Rust-for-Linux project has extensive examples.
Test 2: A Network Driver (virtio-net style)
A simplified network driver handling packet TX/RX with DMA ring buffers, interrupt handling, and NAPI polling.
Source: ~2,400 lines of C Claude’s Rust output: ~3,100 lines of Rust Verdict: Partially correct
Claude handled the high-level structure well: device registration, probe/remove lifecycle, interrupt handler registration. The NAPI polling loop was structurally correct. DMA buffer management was where it struggled.
The C version uses dma_alloc_coherent and manages physical/virtual address pairs manually. Claude translated this to Rust but could not enforce the invariant that DMA buffers must not be freed while the device might still write to them. In C, this is managed by convention and careful ordering in the teardown path. In Rust, this should be enforced by the type system – but Claude produced a translation that used unsafe blocks to bypass the borrow checker rather than designing types that make the invariant unrepresentable.
This is the core translation problem. Correct C-to-Rust translation is not syntactic. It requires understanding the invariants that the C code enforces by programmer discipline and encoding them into Rust’s type system. Claude translates the code. A human is needed to translate the invariants.
Test 3: A Filesystem (simplified ext2-style)
A minimal read-only filesystem with superblock parsing, inode lookup, directory traversal, and block reading.
Source: ~4,800 lines of C Claude’s Rust output: ~5,600 lines of Rust Verdict: Structurally sound, semantically incomplete
The superblock parsing and inode reading were correct – these are essentially data structure translations with endianness handling, and Claude did this flawlessly. Directory traversal worked. Block allocation was read-only so the hard parts (journaling, write ordering, fsync semantics) were not tested.
The problem appeared in VFS integration. The Linux VFS layer expects filesystem implementations to set function pointers in struct super_operations, struct inode_operations, and struct file_operations. Claude mapped these to Rust traits, but the lifetime annotations were wrong – it created self-referential structures where the superblock held references to inodes that held references back to the superblock. This compiles in C (raw pointers, programmer manages lifetime) and does not compile in Rust (the borrow checker rejects the cycle).
The correct solution involves Arc<T> for shared ownership or Pin<Box<T>> for self-referential types. Claude did not arrive at this without explicit prompting about the ownership graph. When I described the lifetime relationships in the prompt, it produced a correct Arc-based design on the second attempt.
The Hard Walls
Wall 1: Inline Assembly
The kernel contains ~50,000 lines of inline assembly across architecture-specific paths. Context switches, interrupt handlers, TLB flushes, memory barriers, and atomic operations all require assembly. Rust supports inline assembly via core::arch::asm!, but the translation is not mechanical – x86 inline assembly in GCC uses AT&T syntax with different constraint letters than Rust’s LLVM-based inline assembly. Claude frequently produced assembly that mixed the two syntaxes.
For a project of this scope, the inline assembly would need to be translated by architecture specialists, not an LLM.
Wall 2: Macro-Heavy Code
The kernel’s macro system is not just syntactic sugar. Macros like container_of, list_for_each_entry, and READ_ONCE/WRITE_ONCE encode safety contracts. container_of performs unsafe pointer arithmetic that relies on struct layout guarantees. READ_ONCE and WRITE_ONCE enforce volatile semantics for data races. The lock macros (spin_lock_irqsave, rcu_read_lock) encode critical section boundaries.
Claude translates the macro expansions, not the contracts. It converts container_of to unsafe pointer math in Rust, which is correct in the narrow sense but misses the opportunity to encode the container relationship in the type system. A human Rust developer would use a generic Container<T> type or a derive macro that makes the relationship checkable at compile time.
Wall 3: Concurrency Model
The Linux kernel’s concurrency model is built on spinlocks, RCU (Read-Copy-Update), atomic operations, and memory ordering guarantees that vary by architecture. RCU is particularly difficult: readers access shared data without locks by deferring destruction until all possible readers have finished. The “all possible readers have finished” part is determined by tracking grace periods across CPUs.
Rust’s ownership model can express some of these patterns (the kernel crate has Mutex<T>, SpinLock<T>, and work on RCU abstractions), but Claude consistently fell back to wrapping C-style RCU patterns in unsafe rather than using Rust’s type system to enforce the read/grace-period contract. Every unsafe block is a place where the Rust rewrite provides no more safety than the C original.
Wall 4: Behavioral Equivalence
The hardest problem is not producing Rust that compiles. It is producing Rust that behaves identically to the C under all conditions – including edge cases that are not documented because they were discovered empirically over 30 years of deployment on billions of devices.
The ext4 filesystem has specific behavior when a write is interrupted by a power failure mid-journal-commit. The TCP stack has specific behavior when receiving a RST during TIME_WAIT with a sequence number that falls in a specific window. The scheduler has specific behavior when a real-time task’s priority ceiling conflicts with a PI-boosted mutex holder on a different CPU.
These behaviors are not in comments. They are not in documentation. They exist only in the code and in the test suites (which are themselves incomplete). Claude cannot translate behavior it cannot observe. A syntactically correct Rust translation that changes any of these behaviors is a regression, and some of these regressions would only surface under production load on specific hardware.
The Token Economics
Setting aside the hard walls, the raw economics are interesting.
A conservative estimate for translating the kernel’s ~36M lines of C:
- Average of 1.5 tokens per line of C (including headers) → ~54M input tokens per pass
- Multiple passes needed (initial translation, review feedback, iteration) → ~150M input tokens total
- Output roughly equal to input → ~150M output tokens
- Claude Opus API pricing: $15/M input, $75/M output (as of early 2026)
- Estimated API cost: $13.5M in token costs alone
That is not a typo. Thirteen million dollars in API costs to produce a first draft that would still require thousands of expert-hours of review.
For comparison, the US Department of Defense spends roughly $15M/year on Linux kernel security hardening through various contracts. The entire annual budget of the Linux Foundation is approximately $250M. An LLM-assisted kernel rewrite would cost a meaningful fraction of the ecosystem’s total funding.
The cost drops dramatically if you scope to the critical subsystems. The core scheduler, memory manager, networking stack, and security modules total ~3.5M lines – roughly 10% of the full kernel. A targeted rewrite of these subsystems would cost ~$1.3M in tokens, which is in the range of a well-funded security initiative.
What This Actually Teaches You
Operation Moonshot is a thought experiment, not a project plan. But the exercise reveals three practical insights about AI-assisted systems programming:
1. Translation Granularity Determines Quality
Claude produces excellent output at the module level (< 5,000 lines with clear API boundaries), acceptable output at the subsystem level (< 50,000 lines with header context), and useless output at the system level (> 100,000 lines with implicit dependencies). This applies beyond kernels – any large C/C++ codebase will follow the same pattern. If you are planning an AI-assisted rewrite or migration, break the work into units that fit the context window with their full dependency closure.
2. Invariant Translation Is the Real Work
Converting C syntax to Rust syntax is mechanical and Claude does it well. Converting C conventions (programmer-enforced invariants, implicit contracts, undocumented ordering requirements) to Rust type system guarantees is the actual engineering challenge. Claude can do this when you describe the invariants explicitly in the prompt. It cannot infer invariants from code alone because invariants are, by definition, the properties that the code maintains but does not state.
This means the most valuable use of an LLM in a rewrite project is not “translate this file.” It is “here is the file and here are the three invariants it maintains – produce a Rust translation where the type system enforces these invariants.” The human’s job shifts from writing the Rust to articulating the invariants.
3. “Correct Translation” Is Not “Drop-In Replacement”
A Rust file that is a correct translation of a C file is not necessarily a correct component of the Rust system it is being inserted into. The interfaces change. The error handling model changes. The memory allocation model changes. Each translated module needs an adapter layer to integrate with the modules around it – whether those neighbors are still in C or already in Rust. The adapter layer is where most bugs will live, and it is the part Claude is worst at generating because it requires understanding both the old system and the new system simultaneously.
What a Realistic Version Looks Like
If I were seriously planning this (I am not), the approach would be:
Phase 1 – Abstractions (Year 1): Expand the Rust-for-Linux kernel crate to cover the full kernel API surface. This is already happening organically. Claude could accelerate it by generating safe wrapper implementations from the C header files, with humans reviewing and correcting the lifetime annotations.
Phase 2 – Leaf Drivers (Years 1-3): Translate the 15,000+ hardware drivers that are essentially leaf nodes in the dependency graph. These have minimal kernel API surface and well-defined behavior. Claude handles these well. Many could be translated with light human review.
Phase 3 – Filesystems (Years 2-4): Translate filesystems starting with simple ones (tmpfs, procfs) and working toward complex ones (ext4, btrfs, XFS). Each filesystem is a self-contained subsystem with a defined VFS interface. Claude needs significant human guidance on the journaling and write-ordering invariants.
Phase 4 – Networking (Years 3-5): The TCP/IP stack, packet filtering, and network device model. This is where behavioral equivalence becomes critical. The test surface for network edge cases is enormous.
Phase 5 – Core (Years 5-8): Scheduler, memory manager, and process model. Last because they are the most interconnected, the most performance-sensitive, and the hardest to test for behavioral equivalence. These likely need to be written by humans with Claude providing initial drafts.
Total estimated timeline: 8-10 years with AI assistance, vs. the 20-30 year horizon the Rust-for-Linux community currently estimates for organic adoption.
The Real Moonshot
The interesting version of this project is not rewriting Linux in Rust. It is using the exercise to build better tools for systems programming with LLMs.
What if the kernel translation project produced:
- A formal invariant language that sits alongside C code and describes the contracts the code maintains, which LLMs can read and enforce in translations?
- A behavioral equivalence test framework that can verify that a Rust translation matches the C original’s behavior under thousands of edge cases, automatically generated from the C code’s control flow?
- A type system mapping tool that takes C patterns (lock ordering, RCU read sections, DMA buffer lifetimes) and generates the corresponding Rust type constraints?
Those tools would be more valuable than the translated kernel itself. They would apply to every C-to-Rust migration, not just Linux.
That is the real moonshot. Not “rewrite Linux.” Rewrite the process of rewriting systems code.
Bottom Line
Can Claude rewrite Linux in Rust? No. Can Claude meaningfully accelerate the Rust-for-Linux effort by producing first-draft translations of bounded modules that humans then review and refine? Yes, today, for the leaf-node components that represent 60%+ of the codebase. The core kernel subsystems remain a human engineering problem – but the scope of that human problem shrinks every time the AI handles another batch of drivers.
The kernel will be rewritten in Rust eventually. The question is whether it takes 25 years of organic effort or 10 years of AI-accelerated effort. Operation Moonshot says the answer depends on how well we can articulate the invariants that 30 years of C programmers carried in their heads.