Rust 调用 C 函数的 FFI

Rust 调用 C 函数的 FFI

🧭 基础场景:调用 C 提供的两个函数

我们从一个简单的 C 库开始:

c 复制代码
// libmath.h
int add(int a, int b);
int subtract(int a, int b);
c 复制代码
// libmath.c
int add(int a, int b) {
    return a + b;
}

int subtract(int a, int b) {
    return a - b;
}

我们希望在 Rust 中调用 addsubtract 函数。


✅ 最佳实践一:用 extern "C" 声明 C 函数

推荐结构:单独放在 ffi/bindings.rs 文件中

rust 复制代码
// 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 出现在业务代码中!

rust 复制代码
// 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

rust 复制代码
pub mod bindings;
pub mod wrapper;

✅ 最佳实践四:使用 cc crate 自动编译 C 代码

手动编译 .a.so 文件容易出错,推荐使用 Rust 官方的 cc crate:

rust 复制代码
// build.rs
fn main() {
    cc::Build::new()
        .file("libmath.c")
        .compile("math");
}

cc 会自动为你调用 gcc 编译 C 文件,并将其链接到 Rust 项目中。


✅ 最佳实践五:主程序只用安全接口

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

bash 复制代码
bindgen libmath.h -o src/ffi/bindings.rs

然后直接使用自动生成的 bindings.rs,无需手写。


✅ 最佳实践七:封装错误处理(可选进阶)

如果 C 函数返回错误码(如 -1 表示失败),可以封装为 Rust 的 Result<T, FfiError>

rust 复制代码
#[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 调用者像操作普通函数一样处理错误,符合语言风格。


🚀 整体结构回顾

text 复制代码
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 风格的安全函数

📚 延伸阅读


相关推荐
虾饺爱下棋40 分钟前
FCN语义分割算法原理与实战
人工智能·python·神经网络·算法
Eloudy4 小时前
简明量子态密度矩阵理论知识点总结
算法·量子力学
点云SLAM4 小时前
Eigen 中矩阵的拼接(Concatenation)与 分块(Block Access)操作使用详解和示例演示
人工智能·线性代数·算法·矩阵·eigen数学工具库·矩阵分块操作·矩阵拼接操作
算法_小学生6 小时前
支持向量机(SVM)完整解析:原理 + 推导 + 核方法 + 实战
算法·机器学习·支持向量机
iamlujingtao6 小时前
js多边形算法:获取多边形中心点,且必定在多边形内部
javascript·算法
算法_小学生6 小时前
逻辑回归(Logistic Regression)详解:从原理到实战一站式掌握
算法·机器学习·逻辑回归
慕容白 MU6 小时前
新唐51单片机看门狗配置流程
c语言·单片机·嵌入式硬件·51单片机
DebugKitty7 小时前
C语言14-指针4-二维数组传参、指针数组传参、viod*指针
c语言·开发语言·算法·指针传参·void指针·数组指针传参
qystca7 小时前
MC0241防火墙
算法
邹诗钰-电子信息工程8 小时前
嵌入式基础知识复习(C语言)
linux·c语言·vim