A line-by-line translation of the OCaml runtime from C to Rust - Learning - OCaml
= 40rem)" rel="stylesheet" data-target="desktop" />
= 40rem)" rel="stylesheet" data-target="chat_desktop" /><br>= 40rem)" rel="stylesheet" data-target="discourse-ai_desktop" /><br>= 40rem)" rel="stylesheet" data-target="discourse-calendar_desktop" /><br>= 40rem)" rel="stylesheet" data-target="poll_desktop" />
A line-by-line translation of the OCaml runtime from C to Rust
Learning
vibecoding
mbacarella
June 10, 2026, 4:37am
The Rust community owes the OCaml community a debt of gratitude, the first Rust compiler was written in OCaml. In this post we rewrite the OCaml runtime in Rust. Consider it our down payment on repaying the debt.
No Marks Shinwell were harmed[1]
More seriously
The OCaml runtime is written in C. I present a port of the C to Rust. It works. It passes the OCaml compiler’s test suite. The test suite is upstream’s, unmodified. The OCaml compiler with the Rust runtime can build itself, can build dune, install an opam switch, and build arbitrary OCaml programs in both bytecode and native. There’s no C left in the runtime. The performance is… surprising! The most fun part was what it taught me about OCaml.
The fork is here https://github.com/mbacarella/rustcaml, if you want to check it out. You just clone the repo, be on the rust-runtime branch and run the normal build instructions. Make sure you have Rust cargo installed. There’s also a rust-runtime-nightly branch that runs the interpreter faster. More on these below.
Consider the rest of this post more as an experience report and proto case-study of a human steered AI driven rewrite of a mature, sophisticated codebase: the OCaml runtime.
The way this was done was not by saying “yo Claude, spin up an agent swarm or whatever, you’re going to greenfield design and implement an OCaml runtime from scratch in Rust. Make no mistakes”. Rather, the instructions were more like “lets file-by-file, line-by-line translate the C code to equivalent Rust code and check with me every step of the way”.
Despite fairly low-tech orchestration, normal person token budget and Claude’s protests, it worked!
Wait, isn’t OCaml written in OCaml?
Quick detour for readers wondering why OCaml isn’t written in OCaml. Most of it is! The compiler, the standard library and the tooling are. But the runtime is written in C. The source tree ships with boot/ocamlc, a bytecode build of the compiler from a previous version. To build OCaml from source, you first build a C program called ocamlrun that knows how to execute OCaml bytecode. Then you use ocamlrun + boot/ocamlc to compile the current sources into native ocamlc. Why not write the runtime in OCaml too? You could, but one reason not to is then you’d have to ship prebuilt binaries for every architecture and OS you plan to support, forever. There are other good reasons too, which will be explored below.
A word on methodology
I used Claude Code for this with the Opus 4.7[2] model on an x86-64 Linux box. Claude did most of the tactical work: coding and presenting a menu of choices on where to go next, and running the tests. I did the steering and some pep talking (more discussed below).
The Bun project recently reported porting 960k(!) lines of Zig to Rust, which generated a fair bit of, let’s say… excitement. I’ll confess my eyes glaze over the moment I see “JavaScript” so I come to this kind of late. But the comparison is instructive because of the differences: Bun runs on JavaScriptCore, so the garbage collector and the interpreter are C++ and were never in scope for the Rust port. Gnarly as that rewrite was, it’s kind of machinery wrapped around a pre-existing engine. It side-steps porting the really interesting part, IMO.
So, that makes this project different. The OCaml runtime is an expertly engine written C: sophisticated multicore machinery, a real garbage collector, FFI contracts that have to be honored to the byte. None of this stuff is part of the normal programmer experience. How would an LLM do at porting this? I wanted to know!
The plan was simple but I think worth spelling out. Make sure the compiler is never not able to run. Put a per-file toggle in the build: flip it and the linker selects the Rust version of a file instead of the C version. This let us port one file at a time, run the full test suite after each flip, and commit a known-good state before moving on.
Each .c file was effectively line-by-line translated to a .rs file as closely as could be done, as that would make it easiest to track and incorporate upstream changes in OCaml. The port can grow with older brother OCaml.
The purpose of the git commit after each port was so that if we get really stuck in the near or far future, like we discover an application that trips an extremely obscure bug, we’ll be able to bisect to any point in time to figure out where in the porting effort the divergence was introduced. The OCaml runtime was never not in a state...