Bridging the Language Gap: Eurydice Turns Rust into Readable C
Share this article
Eurydice: A Rust‑to‑C Compiler That Keeps Your Codebase in One Place
In a world where Rust is steadily becoming the language of choice for new systems code, the reality of existing C ecosystems remains a stubborn barrier. The new compiler Eurydice tackles this head‑on by translating Rust’s mid‑level intermediate representation (MIR) into readable C, allowing developers to keep a single source of truth while still deploying to environments that only understand C.
Why Rust‑to‑C Matters
Rust’s adoption curve is steep, but the infrastructure that supports it is uneven. Open‑source libraries that must run on a multitude of platforms—from RHEL LTS releases to obscure embedded toolchains—often find that one target simply does not support Rust. In such cases, a gradual migration strategy is preferable to a hard fork. Eurydice’s approach satisfies three key needs:
- Gradual transition – The Rust codebase remains authoritative; the C output is regenerated automatically, ensuring that the two stay in sync.
- Single‑source maintenance – Developers modify Rust only; the C layer is a deterministic derivative.
- User‑base safety – Legacy users can continue to compile the C version while the project moves toward Rust’s safety guarantees.
The Core Design
Eurydice plugs into the Rust compiler at the MIR level using the Charon infrastructure, which abstracts the heavy lifting of parsing and normalizing Rust code. This choice gives two advantages:
- Semantic fidelity – By avoiding syntactic sugar, the translation preserves Rust semantics more accurately.
- Reduced surface area – Fewer constructs need to be handled, simplifying the compiler’s logic.
The translation pipeline is roughly:
- MIR → KaRaMeL AST – A ~3,000‑line OCaml module converts the MIR AST into an internal representation.
- Nanopasses – Around 30 small passes simplify the AST, perform monomorphization, and eliminate MIR’s uninitialized‑variable pattern.
- C code generation – The final pass emits C11/C++20‑compatible code, carefully handling Rust features such as pattern matching, array repeat expressions, and DSTs.
“Because arrays in Rust are values, we wrap them within C structs to give them value semantics in C, too.” – Jonathan Protzenko, Eurydice author.
Readable C, Not Boilerplate
A common criticism of language transpilers is that the output is unreadable. Eurydice counters this by:
- Translating Rust structs to C structs, including DSTs via flexible array members.
- Eliminating unnecessary tags in single‑variant enums.
- Using macros to expose generic operations (e.g.,
Eurydice_array_eq) instead of generating dozens of monomorphized functions. - Leveraging Charon’s control‑flow reconstruction to avoid a flood of
gotostatements.
The result is C code that developers can read, debug, and maintain—though it remains verbose due to Rust’s whole‑program monomorphization.
Challenges and Trade‑offs
Eurydice’s design is not without compromises:
- Layout mismatches – Object layouts in C may differ from Rust; developers should compile with
-fno-strict-aliasingto mitigate strict‑aliasing violations. - Platform‑specific monomorphization – Code that depends on CPU‑specific types (e.g., AVX2) must be split into separate files, complicating build scripts.
- Limited standard library support – While Eurydice can translate user code, the full Rust standard library remains out of reach until 2026, when the project aims to expose it entirely.
Community and Roadmap
The project has attracted contributions from GitHub users such as @ssyram and @lin23299, and is actively integrating with Microsoft’s and Google’s crypto libraries. Future milestones include:
- Dynamic trait support via vtables.
- Full Charon monomorphization to replace Eurydice’s custom procedure.
- Standard library extraction by 2026.
“An ambitious goal is for the whole standard library of Rust to be extractable via Eurydice in 2026.” – Protzenko.
Final Thoughts
Eurydice represents a pragmatic bridge between Rust’s modern safety model and the entrenched C ecosystems that still dominate many critical systems. By keeping the Rust source authoritative and automating the C translation, it offers a low‑friction migration path that respects both developer productivity and user‑base stability. As the project matures, it may well become a standard part of the Rust toolchain for libraries that must remain cross‑platform.
Source: Jonathan Protzenko, "Eurydice", October 28, 2025 – https://jonathan.protzenko.fr/2025/10/28/eurydice.html