Rust FFI转换工具库ffi-convert-derive的使用:简化C/C++与Rust的跨语言接口开发
Rust FFI转换工具库ffi-convert-derive的使用:简化C/C++与Rust的跨语言接口开发
ffi-convert是一个用于简化Rust和C兼容数据结构之间转换的工具库,包含了一系列实用工具(函数、特性、数据结构等)。
许可证
可选择以下任一许可证:
- Apache License, Version 2.0
- MIT license
贡献
除非您明确说明,否则任何有意提交包含在作品中的贡献,如Apache-2.0许可证中所定义,都将按上述双重许可,无需任何附加条款或条件。
安装
在项目目录中运行以下Cargo命令:
cargo add ffi-convert-derive
或在Cargo.toml中添加:
ffi-convert-derive = "0.6.2"
示例代码
以下是一个使用ffi-convert-derive的完整示例:
use ffi_convert::*;
// 定义一个Rust结构体并派生CReprOf
#[derive(AsRust, CReprOf, CDrop)]
#[target_type(MyRustStruct)]
struct MyCStruct {
pub a: i32,
pub b: f64,
pub c: *const u8, // 字符串指针
pub d: usize // 字符串长度
}
// 对应的Rust结构体
#[derive(Debug)]
struct MyRustStruct {
pub a: i32,
pub b: f64,
pub c: String
}
fn main() {
// 创建C结构体实例
let rust_string = "Hello FFI".to_string();
let c_struct = MyCStruct {
a: 42,
b: 3.14,
c: rust_string.as_ptr(),
d: rust_string.len()
};
// 转换为Rust结构体
let rust_struct: MyRustStruct = c_struct.as_rust().unwrap();
println!("Converted struct: {:?}", rust_struct);
// 从Rust结构体创建C结构体
let new_rust_struct = MyRustStruct {
a: 100,
b: 1.618,
c: "Golden Ratio".to_string()
};
let c_repr = MyCStruct::c_repr_of(new_rust_struct).unwrap();
// 使用完毕后需要释放资源
c_repr.c_drop();
}
这个示例展示了如何:
- 定义一个C兼容的结构体并派生必要的trait
- 将C结构体转换为Rust结构体
- 从Rust结构体创建C结构体
- 正确处理资源释放
完整示例demo
以下是一个更完整的FFI交互示例,展示了如何在Rust和C之间进行双向数据转换:
use ffi_convert::*;
use std::ffi::CString;
// C兼容的结构体
#[derive(AsRust, CReprOf, CDrop)]
#[target_type(Person)]
struct CPerson {
name: *const libc::c_char,
age: libc::c_int,
height: libc::c_double,
is_student: bool,
}
// Rust原生结构体
#[derive(Debug, Clone)]
struct Person {
name: String,
age: i32,
height: f64,
is_student: bool,
}
fn main() {
// 示例1: 从C结构体创建Rust结构体
let c_name = CString::new("Alice").unwrap();
let c_person = CPerson {
name: c_name.as_ptr(),
age: 30,
height: 1.75,
is_student: false,
};
let rust_person: Person = c_person.as_rust().unwrap();
println!("Converted from C: {:?}", rust_person);
// 示例2: 从Rust结构体创建C结构体
let new_person = Person {
name: "Bob".to_string(),
age: 25,
height: 1.80,
is_student: true,
};
let c_repr = CPerson::c_repr_of(new_person.clone()).unwrap();
// 可以再次转换回Rust结构体验证数据
let converted_back: Person = c_repr.as_rust().unwrap();
println!("Converted back: {:?}", converted_back);
// 释放资源
c_repr.c_drop();
// 示例3: 处理Option类型
#[derive(AsRust, CReprOf, CDrop)]
#[target_type(OptionalData)]
struct COptionalData {
value: *const libc::c_int,
has_value: bool,
}
#[derive(Debug)]
struct OptionalData {
value: Option<i32>,
}
let c_data_with_value = COptionalData {
value: &42 as *const i32,
has_value: true,
};
let c_data_without_value = COptionalData {
value: std::ptr::null(),
has_value: false,
};
println!("With value: {:?}", c_data_with_value.as_rust().unwrap());
println!("Without value: {:?}", c_data_without_value.as_rust().unwrap());
}
这个完整示例展示了:
- 字符串和其他基本类型的转换
- 布尔值的处理
- 双向转换验证
- Option类型的处理
- 资源管理的最佳实践
文档
更多详细使用说明请参考官方文档。
1 回复
Rust FFI转换工具库ffi-convert-derive的使用:简化C/C++与Rust的跨语言接口开发
介绍
ffi-convert-derive
是一个Rust过程宏库,旨在简化Rust与C/C++之间的FFI(外部函数接口)开发。它通过自动生成类型转换代码,减少了手动编写unsafe转换逻辑的工作量,使跨语言接口开发更加安全和便捷。
该库特别适合以下场景:
- 为现有C/C++库创建Rust绑定
- 在Rust项目中暴露API供其他语言调用
- 构建混合语言系统时处理类型转换
主要特性
- 自动为结构体生成C兼容的布局
- 提供类型间的自动转换
- 支持指针和引用类型的处理
- 简化错误处理在FFI边界上的传递
安装
在Cargo.toml中添加依赖:
[dependencies]
ffi-convert-derive = "0.1"
基本用法
1. 基本结构体转换
use ffi_convert_derive::{CReprOf, AsRust};
#[derive(Debug, CReprOf, AsRust)]
#[repr(C)]
pub struct Point {
x: i32,
y: i32,
}
这会自动生成:
Point
的C兼容版本PointC
(#[repr©])Point::c_repr_of
方法,将Rust类型转为C类型PointC::as_rust
方法,将C类型转回Rust类型
2. 使用示例
// Rust代码
let rust_point = Point { x: 10, y: 20 };
// 转换为C兼容类型
let c_point = rust_point.c_repr_of();
// 传递给C函数
unsafe {
some_c_function(&c_point);
}
// 从C接收数据
let c_point_received = unsafe { get_point_from_c() };
let rust_point_received = c_point_received.as_rust();
3. 处理字符串
use ffi_convert_derive::{CReprOf, AsRust};
#[derive(Debug, CReprOf, AsRust)]
#[repr(C)]
pub struct Person {
name: String,
age: i32,
}
库会自动处理Rust的String和C的char*之间的转换。
4. 枚举处理
use ffi_convert_derive::{CReprOf, AsRust};
#[derive(Debug, CReprOf, AsRust)]
#[repr(C)]
pub enum Status {
Ok,
Error,
Unknown,
}
5. 错误处理
use ffi_convert_derive::{CReprOf, AsRust, RawPointerConverter};
#[derive(Debug, CReprOf, AsRust, RawPointerConverter)]
pub struct FfiResult<T) {
is_ok: bool,
value: T,
error: String,
}
高级用法
自定义转换逻辑
use ffi_convert_derive::{CReprOf, AsRust};
#[derive(Debug, CReprOf, AsRust)]
#[repr(C)]
pub struct CustomType {
#[convert_with = "convert_custom_field"]
field: SomeComplexType,
}
fn convert_custom_field(value: &SomeComplexType) -> Result<SomeComplexTypeC, FfiConvertError> {
// 自定义转换逻辑
}
处理指针
use ffi_convert_derive::{CReprOf, AsRust, RawPointerConverter};
#[derive(Debug, CReprOf, AsRust, RawPointerConverter)]
pub struct DataWrapper {
data: *mut u8,
len: usize,
}
注意事项
- 所有FFI相关操作都需要在unsafe块中进行
- 确保C端和Rust端的内存管理策略一致
- 对于复杂类型,可能需要手动实现部分转换逻辑
- 注意处理可能发生的panic,避免跨越FFI边界传播
完整示例
use ffi_convert_derive::{CReprOf, AsRust};
#[derive(Debug, CReprOf, AsRust)]
#[repr(C)]
pub struct User {
id: u64,
username: String,
is_active: bool,
}
// 假设这是从C端接收的函数
extern "C" {
fn get_user_from_c() -> UserC;
}
fn main() {
let c_user = unsafe { get_user_from_c() };
let rust_user: User = c_user.as_rust();
println!("Received user: {:?}", rust_user);
// 发送数据到C端
let new_user = User {
id: 42,
username: "rustacean".to_string(),
is_active: true,
};
let c_new_user = new_user.c_repr_of();
unsafe {
send_user_to_c(&c_new_user);
}
}
ffi-convert-derive
极大地简化了Rust与C/C++之间的交互,减少了unsafe代码的编写量,同时保持了类型安全和内存安全。
完整示例demo
下面是一个完整的FFI交互示例,展示了如何在Rust和C之间传递复杂数据结构:
// 导入必要的派生宏
use ffi_convert_derive::{CReprOf, AsRust, RawPointerConverter};
// 定义Rust端的复杂结构体
#[derive(Debug, CReprOf, AsRust, RawPointerConverter)]
#[repr(C)]
pub struct Employee {
id: u64,
full_name: String, // 自动转换为C字符串
department: String,
salary: f64,
skills: Vec<String>, // 自动转换为C字符串数组
is_manager: bool,
}
// 定义FFI结果类型
#[derive(Debug, CReprOf, AsRust)]
#[repr(C)]
pub struct FfiResult<T> {
success: bool,
value: T,
error_message: String,
}
// 假设的C函数声明
extern "C" {
// 从C获取员工信息
fn c_get_employee(employee_id: u64) -> EmployeeC;
// 发送员工信息到C
fn c_save_employee(employee: *const EmployeeC) -> FfiResultC<u64>;
}
fn main() {
// 示例1:从C获取数据
let c_employee = unsafe { c_get_employee(123) };
let rust_employee: Employee = c_employee.as_rust();
println!("Received employee: {:?}", rust_employee);
// 示例2:发送数据到C
let new_employee = Employee {
id: 456,
full_name: "John Doe".to_string(),
department: "Engineering".to_string(),
salary: 85000.0,
skills: vec!["Rust".to_string(), "C++".to_string()],
is_manager: false,
};
let c_employee = new_employee.c_repr_of();
let result = unsafe { c_save_employee(&c_employee) };
let rust_result: FfiResult<u64> = result.as_rust();
if rust_result.success {
println!("Employee saved with ID: {}", rust_result.value);
} else {
println!("Error saving employee: {}", rust_result.error_message);
}
}
这个完整示例展示了:
- 复杂结构体的自动转换
- 字符串和字符串数组的处理
- 自定义结果类型的FFI传输
- 双向数据传递(Rust到C和C到Rust)
- 错误处理机制
使用ffi-convert-derive
后,开发者只需关注业务逻辑,而无需手动处理繁琐的类型转换和内存管理细节。