C to Rust Transpilation: A Noble Quest Doomed by Fundamental Flaws

Google's recent security blog post on integrating Rust into Android has sent shockwaves through the systems programming community. The headline figure? A staggering 1000x reduction in memory vulnerabilities. Alongside this triumph, a striking graph illustrates Rust's dominance over C in safety metrics:

Article illustration 1

Image: Google's security blog chart showing Rust's memory safety advantage. Source: Google Security Blog via kirancodes.me.

Rust has proven its mettle for new codebases, delivering memory safety without sacrificing performance. But the victory lap is premature. Legacy systems remain shackled to C, which dominates existing code. Consider this estimate from GitHut repository data:

Article illustration 3

Image: Pie chart of systems languages, underscoring C's overwhelming presence. Source: kirancodes.me.

This "metric ton of C code," as one researcher puts it, harbors memory bugs ripe for exploitation. Enter the transpilation dream: tools to rewrite C into safe Rust, purging the sins of the past.

The Rise of C-to-Rust Tools

DARPA's TRACTOR program has fueled a cottage industry. Academic papers abound—"Translating C to Safer Rust" by Emre et al., "Ownership Guided C to Rust Translation" by Zhang et al., and "In Rust We Trust" by Ling et al.—while companies like Immunant push practical tools. Large language models (LLMs) are entering the fray, promising automated refactoring.

The state-of-the-art is Immunant's C2Rust, an open-source tool with an online demo. It applies syntactic rules to convert C into Rust-like code. A simple array loop in C:

int arr[10] = {0};
for(int i = 1; i < 10; i++) {
  arr[i] = i + arr[i - 1];
}

Becomes:

let mut arr: [libc::c_int; 10] = [
    0 as libc::c_int, 0, 0, 0, 0, 0, 0, 0, 0, 0
];
let mut i: libc::c_int = 1 as libc::c_int;
while i < 10 as libc::c_int {
    arr[i as usize] = i + arr[(i - 1 as libc::c_int) as usize];
    i += 1;
    i;
}

This is safe Rust, earning Rust's borrow checker guarantees. But pointers shatter the illusion:

void swap(int *a, int *b) {
    int tmp = *a;
    *a = *b;
    *b = tmp;
}

Yields unsafe Rust:

#[no_mangle]
pub unsafe extern "C" fn swap(
    mut a: *mut libc::c_int, 
    mut b: *mut libc::c_int
) {
    let mut tmp: libc::c_int = *a;
    *a = *b;
    *b = tmp;
}

Research targets this pain point, aiming to infer lifetimes and replace raw pointers with references. Yet deeper issues lurk.

Idiomatic Mismatch: C Patterns Defy Rust Abstractions

Idiomatic C resists Rust's idioms. A C linked-list sum:

struct list {
    int vl;
    struct list *next;
};

int sum(struct list *ls) {
    int res = 0;
    for(; ls; ls = ls->next) {
        res = res + ls->vl;
    }
    return res;
}

C2Rust outputs unsafe Rust, pointer-heavy and low-level. A native Rust developer would leverage iterators:

pub fn sum(ls: &List) -> i32 {
    ls.iter().sum()
}

Rust's power stems from abstractions—iterators, options, results—that the type system enforces. Syntactic translation misses this, yielding Rust in name only.

The Correctness Crisis: No Formal Foundation

What does "correct" translation even mean? Transforming a C for loop:

for(int i = 0; i < 10; i++) {
   arr[i] = i;
}

To Rust:

for i in 0..10 {
  arr[i] = i;
}

Feels right. But equivalence demands relating disparate semantics. C mutates a stack variable; Rust iterates a Range. Subtle differences emerge:

for(int i = 0; i < 10; i++) {
  foo(&i);  // Pass &i to arbitrary function
}

Rust's iterator yields temporaries without addresses. C's semantics allow pointer manipulation that Rust forbids. Preserving all C behaviors necessitates unsafe Rust.

Worse: Rust lacks formal semantics. Projects like RustBelt advance, but production Rust defies rigorous proofs. Transpilation relies on "vibes," unfit for critical systems.

Article illustration 2

Image: 'RIP C' meme capturing the legacy burden. Source: kirancodes.me.

A Path Beyond Transpilation

C-to-Rust work yields subproblems ripe for progress—inference, ownership modeling. DARPA and academia drive rigor in niches. Yet wholesale rewriting remains "not even wrong," as the source argues.

Developers must embrace incremental migration: rewrite hotspots in Rust, leverage FFIs cautiously. Tools like C2Rust aid understanding, not salvation. Humanity's C stain endures—no LLM can erase decades of technical debt. The real win lies in forging safer futures, one module at a time.

Source: kirancodes.me - 'Humanity is stained by the sins of C', a PL researcher's critique published November 18, 2025.