Generating Rust bindings with bindgen
In the previous exercise you saw how easy it is for manual bindings to go out of
sync, and the scary silent corruption that causes. This section introduces a
tool that avoids all of this: bindgen. It’s a library you call
from your Rust build script to generate Rust bindings to C code from C header
files. It runs libclang on a C header and emits matching Rust declarations:
extern blocks, #[repr(C)] structs, integer constants. The header remains the
single source of truth, with the Rust bindings being generated on every build.
You call bindgen from your build script like so:
let bindings = bindgen::Builder::default()
.header("c_src/bm_legacy.h")
// tell Cargo to re-run when headers change
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
.generate()
.expect("bindgen failed");
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings.write_to_file(out_path.join("bindings.rs")).unwrap();
This writes the generated file into the crate’s OUT_DIR (usually
target/<debug or release>/build/<crate name and hash>/out). Inspecting the
generated file, you will see something like this:
/* automatically generated by rust-bindgen 0.72.1 */
unsafe extern "C" {
pub fn bm_add(a: ::std::os::raw::c_int, b: ::std::os::raw::c_int) -> ::std::os::raw::c_int;
}
You can then pull this generated file into your crate with the include! macro
like so:
mod sys {
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
}
This is great: we’ve gained end-to-end type checking. A change in the C header
will not silently corrupt our Rust code. But remember, these bindings are as
unsafe as the C code itself. It’s your responsibility to use them correctly.
You would typically wrap them in a safe, idiomatic API. This is the common
-sys crate pattern: a <crate>-sys crate holds the raw bindings, while the
<crate> crate on top exposes the safe abstractions.
Head to the exercise
Replace the hand-written extern block from the previous section with bindgen
in build.rs. The tests stay identical.
Tip
cargo expand -p bm_bindgen shows what bindgen produced. Requires
cargo-expand.
Exercise
The exercise for this section is located in 01_intro/03_bindgen