Rust 调用 C 函数的 FFI
🧭 基础场景:调用 C 提供的两个函数
我们从一个简单的 C 库开始:
// libmath.h
int add(int a, int b);
int subtract(int a, int b);
// libmath.c
int add(int a, int b) {
return a + b;
}
int subtract(int a, int b) {
return a - b;
}
我们希望在 Rust 中调用 add
和 subtract
函数。
✅ 最佳实践一:用 extern "C"
声明 C 函数
推荐结构:单独放在 ffi/bindings.rs
文件中
// src/ffi/bindings.rs
use libc::c_int;
extern "C" {
pub fn add(a: c_int, b: c_int) -> c_int;
pub fn subtract(a: c_int, b: c_int) -> c_int;
}
✅ 使用
libc::c_int
而不是 Rust 的i32
,可确保类型在不同平台对齐。
✅ 最佳实践二:将 unsafe 封装成安全函数
永远不要让 unsafe
出现在业务代码中!
// src/ffi/wrapper.rs
use super::bindings;
pub fn safe_add(a: i32, b: i32) -> i32 {
unsafe { bindings::add(a, b) }
}
pub fn safe_subtract(a: i32, b: i32) -> i32 {
unsafe { bindings::subtract(a, b) }
}
✅ 这样 unsafe 调用被封装在一层薄薄的安全函数中,最大程度保证调用安全。
✅ 最佳实践三:模块结构清晰划分
推荐的模块结构如下:
src/
├── ffi/
│ ├── mod.rs // pub use wrapper
│ ├── bindings.rs // extern "C" 接口绑定
│ └── wrapper.rs // 封装 safe API
├── main.rs // 主程序
build.rs // 编译 libmath.c 的脚本
libmath.c / libmath.h // C 源码
ffi/mod.rs
:
pub mod bindings;
pub mod wrapper;
✅ 最佳实践四:使用 cc crate 自动编译 C 代码
手动编译 .a
或 .so
文件容易出错,推荐使用 Rust 官方的 cc
crate:
// build.rs
fn main() {
cc::Build::new()
.file("libmath.c")
.compile("math");
}
✅
cc
会自动为你调用 gcc 编译 C 文件,并将其链接到 Rust 项目中。
✅ 最佳实践五:主程序只用安全接口
// src/main.rs
mod ffi;
fn main() {
let result = ffi::wrapper::safe_add(10, 20);
println!("10 + 20 = {}", result);
let diff = ffi::wrapper::safe_subtract(30, 15);
println!("30 - 15 = {}", diff);
}
✅ 业务代码中无任何
unsafe
、无任何 extern 细节,一目了然。
✅ 最佳实践六:自动生成绑定(适用于第三方库)
如果你要对接的是大型 C 库(如 SQLite、OpenSSL),手写 extern 声明代价高,容易出错。这时推荐使用 bindgen
:
bindgen libmath.h -o src/ffi/bindings.rs
然后直接使用自动生成的 bindings.rs
,无需手写。
✅ 最佳实践七:封装错误处理(可选进阶)
如果 C 函数返回错误码(如 -1 表示失败),可以封装为 Rust 的 Result<T, FfiError>
:
#[derive(Debug)]
pub enum FfiError {
MathError,
}
pub fn safe_subtract(a: i32, b: i32) -> Result<i32, FfiError> {
let result = unsafe { bindings::subtract(a, b) };
if result < 0 {
Err(FfiError::MathError)
} else {
Ok(result)
}
}
✅ 让 Rust 调用者像操作普通函数一样处理错误,符合语言风格。
🚀 整体结构回顾
project/
├── libmath.c
├── libmath.h
├── build.rs
├── Cargo.toml
└── src/
├── ffi/
│ ├── mod.rs
│ ├── bindings.rs
│ └── wrapper.rs
└── main.rs
🧾 总结:Rust 调用 C 的 FFI 最佳实践
项目 | 最佳做法 |
---|---|
绑定声明 | 用 extern "C" + libc::c_int 明确类型 |
unsafe 封装 | 只在最小封装层使用 unsafe |
模块划分 | 拆分为 bindings + wrapper |
构建方式 | 用 cc crate 自动编译 C 源码 |
错误处理 | 推荐封装为 Result<T, Error> (进阶) |
自动生成绑定 | 用 bindgen 处理大型头文件 |
应用层接口 | 只使用 Rust 风格的安全函数 |