Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Exposing Rust to C with cheadergen

With bindgen we generated the Rust declarations for calling C. Now we go the other way: our goal is to migrate C code to Rust, so C will need to call into our new Rust code.

extern "C" fn and #[unsafe(no_mangle)] take care of the calling convention and the symbol name:

use std::ffi::c_char;

#[unsafe(no_mangle)]
pub extern "C" fn bm_strlower(s: *mut c_char) {
    // ...
}

What’s still missing is a header file that tells the C compiler about the API. You could write it by hand, but you’d be maintaining two sources of truth. cheadergen (the inverse of bindgen) generates C headers from Rust code.

cheadergen, created and maintained by our colleague Luca Palmieri, reads your Rust code and generates a C header for the relevant FFI-visible symbols.

Install the CLI from the cheadergen releases page.

Then you can run the following command in your crate root (for example, the current exercise crate):

cheadergen generate

and a header will appear:

/* Generated by cheadergen — do not edit */

void bm_strlower(char *s);

cheadergen comes with two ways to configure its output. The first is a project-wide cheadergen.toml file. Here you can tweak the style of the generated headers, include custom preamble text, and more. See the full documentation here.

The second configuration option is the #[cheadergen::config(...)] attribute. You can apply this attribute to any Rust type. It lets you configure per-type options such as renaming fields, skipping exporting, forcing exporting, and more. See the full documentation here.

A note on cbindgen

If you’ve been researching Rust FFI tooling, you’ve undoubtedly come across cbindgen, a similar tool maintained by Mozilla, and you may be wondering: “Why did you just introduce me to this custom tool instead?”

Well, we wouldn’t have built a new tool if the existing one fit. cbindgen dates back to 2017, when the Rust ecosystem looked quite different. To read your Rust and emit headers, it had to parse the source itself with a custom syn-based parser. At the time, that was the only way, since the compiler didn’t expose enough information.

But the technique has real limits. The custom parser doesn’t always agree with rustc, it often needs manual steering and fixups, and when it hits something it can’t handle it tends to silently skip generating a header rather than tell you why. None of this is a knock on cbindgen: it’s what the constraints of the era allowed. cheadergen exists because those constraints have since eased, letting us build something with better diagnostics and fewer surprises.

Head to the exercise

You’ll port the bm_strlower function to Rust and call cheadergen generate --lang c --output-dir c_test -p bm_cheadergen to generate the header automatically.

Exercise

The exercise for this section is located in 02_basics/09_cheadergen