Meta's Transition from C to Rust on Mobile
Meta's engineers are rewriting a decades-old C messaging library shared across Messenger, Facebook, Instagram, and AR/VR platforms in Rust, without prior Rust expertise on the team.

The conventional wisdom is that you rewrite systems in Rust when you have Rust experts to do it. Meta sent engineers who had never written Rust into a decades-old C messaging library used by billions of people and asked them to modernize it. The interesting question is whether that was reckless or necessary.
Source
An inside look at Meta’s transition from C to Rust on mobile
By: Meta (Pascal Hartig, Elaine, Buping)
Published: Jul 01 2025
Type: Engineering article
Saved because this is a ground-level account of what a Rust migration feels like mid-flight, not a polished retrospective. The details about learning Rust on the job while rewriting production infrastructure for billions of users are the kind of thing that typically gets edited out of success stories.
Rewriting C you didn’t write, in Rust you hadn’t learned
C is not C++. That distinction matters because most Rust migration stories come from C++ codebases, where engineers can lean on mental models of RAII and deterministic destructors that C++ shares with Rust. A C codebase from the early smartphone era carries different assumptions: manual memory management with no language-level ownership concept, pointer-based interfaces that leave lifetime obligations implicit, and a threading model that relies on programmer discipline rather than language enforcement. The class of bugs this produces is well-documented — use-after-free, buffer overflows, data races — but “well-documented” does not mean “easy to see in a working system.” Code that has run for years without a visible crash has often just been lucky about allocation patterns, not correct about ownership.
The messaging library Meta is rewriting sits at the bottom of several product stacks simultaneously. Messenger, Facebook, and Instagram all share it. So do Meta’s AR/VR platforms, which adds constraints beyond iOS and Android: real-time latency budgets, deterministic frame timing, and memory pressure profiles that differ significantly from phone workloads. A bug in the library is not a bug in one product; it is a bug in everything that library touches at once. That exposure is the strongest argument for a rewrite over incremental patching — not that C is bad in isolation, but that the cost of a memory safety failure is multiplied by every surface the library serves. One mishandled pointer is simultaneously a Messenger bug, a Facebook bug, an Instagram bug, and a headset bug.
What engineers Elaine and Buping describe is the particular discomfort of learning a language while rewriting production code in it. Rust’s ownership model is not analogous to anything in C. In C, a pointer is a number — you can copy it, pass it, store it, and nothing in the language prevents you from using it after the memory it points to has been freed. Rust’s borrow checker is not a stricter linter on that same mental model. It is a different mental model entirely. The pointer is a relationship: it borrows a value for a scope, and the compiler verifies that the value outlives every borrow. Learning that model while simultaneously preserving the behavior of a codebase you are translating from a language with no equivalent concept requires reasoning at two levels at once. The compiler error messages are verbose and helpful — they often contain the fix — but a helpful error is still a thing that stops you from compiling, and early-stage Rust learning is largely a sequence of stops.
The organizing principle Meta’s team describes as “developer happiness” is easy to dismiss as a soft metric, but it points at something real. A codebase the team finds understandable is one the team will maintain with care. A codebase that requires constant defensive reasoning about what the previous engineer might have meant accumulates workarounds and hidden load-bearing assumptions. Rust’s explicit ownership semantics make intent visible in a way that C’s implicit ownership does not — when you read a Rust function signature, the lifetime annotations and reference types tell you who owns what and for how long. That readability is a maintenance asset that compounds over the years a shared library lives in production.
The incremental migration strategy matters too. This is not a stop-the-world rewrite. The C library continues to serve production while the Rust version is built alongside it, with both sides exercised against the same behavioral contracts. That overlap provides safety: the working C code is the behavioral spec, and any deviation in the Rust version surfaces as a divergence rather than a silent regression. It also means the team never loses the ability to ship. The risk of a months-long Rust learning curve is bounded to the migration timeline, not compounded into a prolonged outage window where no fixes can go out. The organizational cost of learning Rust under these conditions is real and measurable; the alternative — perpetuating memory safety risk across four platforms simultaneously — has costs that are harder to see until something goes wrong.
My take
The most underappreciated detail is that the engineers are learning Rust on this project, not before it. That is a deliberate organizational bet: the language is learnable enough, and the tooling helpful enough, that on-the-job acquisition is a viable strategy for production rewrites. Ten years ago that bet would have been unreasonable. It is a quiet data point about where Rust’s learning curve actually sits today, and what the compiler’s error quality buys in practice.
Where this is useful
- Planning a C-to-Rust migration in a shared library where one bug appears simultaneously across multiple products
- Justifying Rust adoption to a team without prior Rust experience — the learning curve is real but the compiler makes it bounded and explicit
- Designing a migration strategy for mobile code that must run identically across Android, iOS, and additional platform targets
- Not applicable when the codebase has a single owner and a small blast radius — the overhead of running both pipelines in parallel is not worth it for isolated single-product code
Apply this: Before estimating a C-to-Rust migration, count the number of distinct products sharing the library — each one multiplies the cost of every memory safety bug you are deferring, and that multiplier is the honest business case for the rewrite.
What I want to remember
Remember: A C library shared across multiple product surfaces carries a hidden multiplier on every memory safety bug — the justification for rewriting it is not correctness in isolation but the number of simultaneous failures a single pointer mistake can produce.