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