





















在 C 与 Rust 的混合开发(FFI)中,FFI 黄金法则是确保跨语言调用时内存安全、二进制对齐、类型收敛以及
零运行时开销的核心准则。它是一套将 Rust 严格的编译期安全与 C 语言自由的底层指针进行完美接轨的工程
规范。
以下是针对开发及通用混合编程的 FFI 黄金法则 深度总结:
跨语言交互的本质不是代码转换,而是内存和寄存器级别的传参约定。
pub extern "C",以强迫 Rust 编译器采用标准的 C 语言调用约定(Calling Convention)。#[no_mangle] 属性,阻止 Rust 编译器混淆函数名,确保 cbindgen 能够识别并生成同名的 C 语言函数声明。#[repr(C)] 属性。这保证了 Rust 结构体中字段的内存顺序、对齐方式(Alignment)与 C 语言的 struct 严格保持 100% 一致。内存泄漏与野指针(Use-After-Free)大多源于所有权边界的模糊。
Box::new() 申请的堆内存,如果转换成裸指针 *mut c_void 传给 C 侧,C 侧绝对不能调用 free() 去释放它。必须在生命周期结束时,将指针传回 Rust 侧,通过 Box::from_raw() 重新收回所有权并触发 Rust 的自动析构(Drop)。return &mut *cntx。必须通过指针级联强转(如 cntx as *mut _ as *mut c_void)或使用 Box::into_raw() 传递纯粹的内存地址。将 unsafe 的污染范围控制在最小的单元内。
pub unsafe extern "C" fn。这会导致外部强类型检查失败。unsafe,将其声明为普通的 pub extern "C" fn。仅在函数体内部需要处理 C 指针、执行 transmute 或解引用时,才开启 unsafe { ... } 代码块。NULL,在 Rust 侧的回调定义中可以直接用 &mut SpaceShipState 代替 *mut _。Rust 框架会在入口处自动完成转换,使你在函数内部无需编写任何指针判空代码,直接享受 Safe Rust 的开发体验。数据处理性能的生死线在于避免内存拷贝。
const uint8_t *input 和 uint32_t input_len 传给 Rust 时,Rust 侧严禁将其分配(Allocate)到新的 Vec<u8> 中。slice::from_raw_parts将其打包为 Rust 的切片 &[u8]。这只是创建了一个内存视窗(Window),开销为零,却能直接调用 Rust 强大的模式匹配和解析工具(如 nom 框架)。参考实现:
#[macro_export]
macro_rules! build_slice {
($buf:ident, $len:expr) => {
if $buf.is_null() && $len == 0 {
&[]
} else {
unsafe { std::slice::from_raw_parts($buf, $len) }
}
};
}
利用自动化工具(cbindgen)时,要学会“防御性编程”,防止类型污染。
*const QuantumContext 而 C 侧老代码写死要求 QuantumContext * 时,不要强行修改任何一侧。在 Rust 侧声明一个别名 type QuantumContextPtr = *const core::QuantumContext;。cbindgen.toml 中配置 [export.rename] "QuantumContextPtr" = "QuantumContext *"。这样既满足了 Rust 侧的强类型回调注册,又强行让 cbindgen 在 C 头文件中吐出不带 const 的干净指针,实现了对既有老 C 代码的零侵入、零破坏。注意:
如果 C 侧调用该函数时传入的是普通指针(*),而导出的头文件里声明的是常量指针(*const),这种情况下确实完全不需要使用类型别名去抹平冲突。
FFI 黄金法则的核心思想可以浓缩为一句话:“在 C 的世界里展现 C 的规矩(提供统一的 void* 和 int 返回码),在 Rust 的世界里恪守 Rust 的方圆(内部强转为安全引用与 Safe 闭包)”。
参考资料:
rust工程化实践卷III juler
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。