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 且类型是基础类型(如 usizei32 等)。
  • 转换:默认生成为 #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 中明确配置映射关系。
  • 注释迁移:
    • cbindgen 会自动将 Rust 的文档注释(///)转换为 C 风格的注释(/* ... */),极大地提升了 C 端开发者的体验。

4. 配置文件 cbindgen.toml 的干预

可以通过该文件精细控制导出行为:
  • include / exclude:白名单或黑名单过滤。
  • language:指定生成 C 还是 C++ 代码。
  • namespace:为 C++ 代码自动包裹命名空间。
  • braces:控制大括号换行风格。
总结
三位一体原则:如果想让一个 Rust 项出现在头文件中,请确保它满足:pub 可见性 + #[repr(C)] 布局 + 基础类型兼容。
如果某些复杂逻辑无法直接导出,通用的做法是编写一个专用的 FFI 适配层模块,该模块使用基础指针(Raw Pointers)和基础类型重新包装业务逻辑。

 

posted @ 2026-01-16 17:21  PKICA  阅读(3)  评论(0)    收藏  举报