rust导出规则
在Rust 跨语言开发体系中,
cbindgen 是连接 Rust 与 C/C++ 的核心工具。它并不是盲目地导出所有内容,而是基于一套严格的匹配规则。只有满足以下条件的 Rust 代码才会被
cbindgen 识别并导出到头文件中:1. 核心触发条件
- 可见性:必须是
pub(公有)。私有项会被完全忽略。 - 内存布局:必须标注
#[repr(C)]。这是告诉编译器:“请按照 C 语言的标准对齐内存,不要进行 Rust 特有的优化”。 - 符号链接:函数必须标注
#[no_mangle](防止编译器改名)和extern "C"(指定 C 语言调用约定)。
2. 具体导出规则清单
A. 结构体 (Structs)
- 导出对象:标注了
#[repr(C)]的pub struct。 - 注意:
- 字段也必须是
pub且是 C 兼容类型。 - 泛型结构体:会被导出为针对特定类型的具体结构体(需显式指明实例化)。
- 不透明结构体:如果没有
#[repr(C)]但被函数接口引用,cbindgen会将其生成为前置声明(Forward Declaration),C 端仅能以指针形式持有,无法访问内部字段。
- 字段也必须是
B. 枚举 (Enums)
- 简单枚举:导出的结果是标准的 C
enum。 - 带字段枚举(Tagged Unions):必须标注
#[repr(C)]或#[repr(u8/u32)]。cbindgen会将其转化为一个包含tag(标识符)和union(数据负载)的复杂结构体。
C. 函数 (Functions)
- 规则:
#[no_mangle] pub extern "C" fn my_function(arg: *const MyStruct) -> i32 { ... } cbindgen会根据参数和返回值类型生成对应的函数原型。
D. 常量 (Constants)
- 规则:
pub const且类型是基础类型(如usize,i32等)。 - 转换:默认生成为
#define。如果在cbindgen.toml中配置,也可以生成为static const或constexpr(C++)。
E. 类型别名 (Type Aliases)
- 规则:
pub type。 - 转换:生成为
typedef。
3. 特殊处理规则
- 字符串与容器:
String和Vec不能直接导出。- 解决办法:在 Rust 端将其封装为
*mut c_char或自定义的struct { data: *mut u8, len: usize },然后再导出。
- 布尔值:
- Rust 的
bool在 C 中通常被对应为bool(C99 之后)或uint8_t。建议在cbindgen.toml中明确配置映射关系。
- Rust 的
- 注释迁移:
cbindgen会自动将 Rust 的文档注释(///)转换为 C 风格的注释(/* ... */),极大地提升了 C 端开发者的体验。
4. 配置文件 cbindgen.toml 的干预
可以通过该文件精细控制导出行为:
include/exclude:白名单或黑名单过滤。language:指定生成C还是C++代码。namespace:为 C++ 代码自动包裹命名空间。braces:控制大括号换行风格。
总结
三位一体原则:如果想让一个 Rust 项出现在头文件中,请确保它满足:
pub 可见性 + #[repr(C)] 布局 + 基础类型兼容。如果某些复杂逻辑无法直接导出,通用的做法是编写一个专用的 FFI 适配层模块,该模块使用基础指针(Raw Pointers)和基础类型重新包装业务逻辑。
浙公网安备 33010602011771号