Rust bindgen:为 C 头文件生成 Rust 调用绑定
2025年8月26日Elecmonkey
基本用法
命令行方式
首先安装 bindgen 可执行文件:
bash1cargo install bindgen-cli
然后运行绑定生成:
bash1bindgen input.h -o bindings.rs
其中 input.h
是你的 C 头文件,bindings.rs
是生成的 Rust 绑定。如果需要传递包含目录或宏定义,可以在命令后加 --
及相应 clang 参数(例如 -I
)。
库调用方式(build.rs)
在 Cargo.toml
的 [build-dependencies]
中加入 bindgen,然后在 build.rs
中使用:
rust1use std::env; 2use std::path::PathBuf; 3use bindgen; 4 5fn main() { 6 let bindings = bindgen::Builder::default() 7 .header("wrapper.h") 8 .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) 9 .generate() 10 .expect("无法生成绑定"); 11 12 bindings.write_to_file(PathBuf::from(env!("OUT_DIR")).join("bindings.rs")) 13 .expect("无法写入绑定文件"); 14}
在 Rust 代码中通过 include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
来包含生成的绑定。
Clang/Libclang 依赖
bindgen 依赖 libclang(Clang 9.0+)
- Windows: 使用
winget install LLVM.LLVM
,设置LIBCLANG_PATH
为 LLVM 安装目录的 bin 路径 - Linux (Debian/Ubuntu):
apt install libclang-dev
- macOS: 使用 Homebrew 安装 llvm
复杂 C 类型支持
结构体和联合体
- 结构体: 直接翻译为带
#[repr(C)]
的 Rust 结构体 - 联合体: 如果所有字段都可 Copy,生成原生 union;否则生成
__BindgenUnionField
包装
枚举处理
rust1// 生成新类型加关联常量 2.newtype_enum("MyEnum") 3 4// 生成真正的 Rust enum(注意越界风险) 5.rustified_enum("MyEnum") 6 7// 位标志枚举 8.bitfield_enum("MyFlags")
函数指针
C 中的函数指针类型会转换为 Option<extern "C" fn(...)>
,支持空指针情况。
位域处理
Rust 不原生支持 C 结构体中的位域,bindgen 会生成访问器方法:
rust1// C: struct { unsigned a:1; unsigned b:1; unsigned c:2; }; 2let mut bf = unsafe { create_bitfield() }; 3bf.set_a(1); 4println!("a={}", bf.a()); 5bf.set_b(1); 6bf.set_c(3);
构建时自动生成绑定
在 build.rs
中动态生成绑定:
rust1use std::env; 2use std::path::PathBuf; 3use bindgen; 4 5fn main() { 6 let header = "wrapper.h"; 7 let bindings = bindgen::Builder::default() 8 .header(header) 9 .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) 10 // 可选:指定 clang 参数 11 //.clang_args(&["-I/path/to/include"]) 12 // 可选:只生成需要的符号 13 //.allowlist_function("foo_.*") 14 //.allowlist_type("MyStruct") 15 .generate() 16 .expect("无法生成绑定"); 17 18 let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("bindings.rs"); 19 bindings.write_to_file(out_path).expect("写入绑定文件失败"); 20 21 // 链接 C 库 22 println!("cargo:rustc-link-search=native=/path/to/lib"); 23 println!("cargo:rustc-link-lib=mylib"); 24}
FFI 安全性
unsafe 使用
外部函数和指针操作放在 unsafe { }
块中:
rust1unsafe { 2 let result = c_function(ptr); 3}
签名正确性
Rust 无法在编译时验证 FFI 函数签名,必须使用正确的 C 类型(使用 libc 里的 c_int
、c_char
等别名)。
内存和所有权
处理指针时明确内存所有权和生命周期:
rust1// 字符串转换示例 2use std::ffi::{CString, CStr}; 3 4let c_string = CString::new("Hello").unwrap(); 5let ptr = c_string.as_ptr(); 6// 调用 C 函数...
空指针检查
指针处理为 Option<fn>
返回,强迫调用方进行空指针检查。
错误处理
把 C 函数的错误表示包装成 Rust 中常用的错误处理枚举 —— Result
或 Option
。