Use cbindgen to automatically generate C-compatible header files from your Rust code by configuring a cbindgen.toml file and running the tool with your crate path. This ensures your C/C++ consumers get accurate function signatures, types, and memory layout definitions without manual maintenance.
First, add cbindgen as a dev dependency or install it globally via cargo install cbindgen. Create a cbindgen.toml in your project root to define which items to export and how to format them. The tool scans your Rust code for items marked with #[no_mangle] and pub extern "C", then generates a .h file that matches your Rust ABI.
Here is a minimal cbindgen.toml configuration that exports all public C-compatible functions and types:
language = "C"
cpp_compat = true
export = { include = ["*"], exclude = ["internal_*"] }
parse = { expand = true }
In your Rust source (lib.rs), define your FFI interface using the standard extern "C" block. Note that cbindgen relies on the #[no_mangle] attribute to ensure the symbol name matches exactly what the C header expects.
// src/lib.rs
#[no_mangle]
pub extern "C" fn add(a: i32, b: i32) -> i32 {
a + b
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct Point {
x: f64,
y: f64,
}
#[no_mangle]
pub extern "C" fn point_distance(p1: &Point, p2: &Point) -> f64 {
let dx = p1.x - p2.x;
let dy = p1.y - p2.y;
(dx * dx + dy * dy).sqrt()
}
To generate the header, run the following command from your project root:
cargo cbindgen --output bindings.h
If you installed cbindgen globally, use cbindgen --crate <crate_name> --output bindings.h. The resulting bindings.h will contain the correct C declarations, including the Point struct with proper #pragma pack directives if needed, and the function prototypes.
For CI/CD pipelines, you can automate this by adding a build step that regenerates the header whenever Rust code changes. If you need to include specific Rust types in the header that aren't directly exposed in FFI functions, ensure they are marked pub and included in the export configuration. Always verify the generated header compiles against your C code, as complex generic types or Rust-specific traits may require manual adjustments or custom cbindgen annotations.