In November 2025, Google disclosed a set of numbers that silenced the entire security community:

  • Memory-safety vulnerabilities on Android dropped below 20% of all vulnerabilities for the first time — before the Rust migration, this class had long been the dominant majority.
  • Rust code carries about 0.2 memory-safety bugs per million lines versus roughly 1000 for C/C++ — a gap of about 1000x.
  • As a bonus: Rust changes had a rollback rate only one-quarter of C++’s, and code-review time was 25% shorter — the safer way to write code turned out to be the faster way too.

This isn’t marketing — it’s the conclusion of Android’s team cross-comparing 5 million lines of Rust against an equivalent volume of C++. This article breaks down why it holds, and what playbook you can copy.

Entire bug classes, eliminated at compile time

The headliners of memory-safety bugs — dangling pointers (use-after-free), double-free, buffer overflow, data races — are fundamentally “accessing memory you shouldn’t.” Rust’s ownership + borrow checking + lifetimes forbid these patterns at compile time:

  • Every piece of memory has either one mutable borrow or several immutable borrows at any instant, eliminating data races;
  • once a value is moved or freed, the old reference can’t be used, eliminating use-after-free;
  • slice/index access stays in bounds, leaving buffer overflow nowhere to hide.

C/C++ leave all of these checks to runtime and to your own discipline; Rust hands them to the compiler.

Hands-on comparison: use-after-free

The same “use after free” has completely different endings in C and Rust.

C version (undefined behavior, potentially exploitable):

char *buf = malloc(16);
free(buf);
// Undefined behavior: buf is freed but still used
strcpy(buf, "hello"); // may overwrite arbitrary memory, forming an exploit primitive

The compiler says nothing. The program might run fine, or it might turn into remote code execution after some unrelated update.

Rust version (rejected at compile time):

let mut buf = vec![0u8; 16];
drop(buf);
// buf.push(1); // ❌ compile error: borrow of moved value

Even without an explicit drop, the moment you pass buf’s reference out of a function or its lifetime conflicts, the borrow checker stops you at compile time. There is no “discovered at runtime” step.

Another classic C trap — returning a reference to a local buffer — fails to compile in Rust:

fn build() -> &'static [u8] {
    let data = [1u8, 2, 3];
    &data // ❌ compile error: returns reference to local variable
}

Real-world case: Rust can still break, but the defense is denser

Rust isn’t a silver bullet. Google found a linear buffer overflow in CrabbyAVIF — an AVIF image parser written in unsafe Rust — tracked as CVE-2025-48530, CVSS 8.1, theoretically enabling remote code execution.

But it was never exploited — because Android’s Scudo hardened allocator places guard pages around buffer regions, turning the overflow into an observable crash that was caught during testing. This “near-miss” teaches two things:

  1. unsafe blocks must still be held to safe-code standards — write // SAFETY: comments and run fuzz tests;
  2. language safety + allocator/sanitizer defense-in-depth beats relying on the language alone.

Google’s own words: unsafe Rust “is already really quite safe,” its bug density is still far below C/C++, and “unsafe” does not automatically switch off the language’s safety checks.

The playbook you can copy

  1. New code defaults to Rust: especially high-risk zones like parsers, network protocols, and system internals.
  2. Rewrite high-risk legacy components in Rust: image (PNG/AVIF), font, JSON, and compression parsers are CVE magnets. Chromium has already swapped its PNG, JSON, and web-font parsers for Rust implementations.
  3. unsafe must come with SAFETY comments + fuzzing: every unsafe block documents pointer origin, lifetime, and invariants; run cargo fuzz over it.
  4. Audit FFI boundaries separately: places that talk to C/C++ are where bugs cluster — enforce boundary checks and clean ownership transfer.
  5. Keep defense-in-depth: even with all-Rust, don’t drop Scudo / AddressSanitizer / fuzzing — they’re the last gate.

Security isn’t “switch languages and sleep easy,” but a 1000x density gap means this: moving high-risk code into Rust trades an entire bug class from “hope humans don’t err” to “the compiler won’t let them.” Google has already done the math for you.