Rust FFI转换库ffi-convert的使用:高效实现Rust与C/C++的数据类型互转与跨语言调用
Rust FFI转换库ffi-convert的使用:高效实现Rust与C/C++的数据类型互转与跨语言调用
简介
ffi-convert是一个实用工具集合(函数、特征、数据结构等),用于简化Rust和C兼容数据结构之间的转换。该库由Sonos开发,采用Apache 2.0或MIT许可证。
安装
在项目目录中运行以下Cargo命令:
cargo add ffi-convert
或在Cargo.toml中添加:
ffi-convert = "0.6.2"
示例代码
以下是使用ffi-convert进行Rust和C/C++数据类型互转的完整示例:
use ffi_convert::*;
// 定义Rust结构体
#[derive(Debug, Clone, CReprOf, AsRust)]
#[repr(C)]
struct Person {
name: String,
age: i32,
}
fn main() {
// Rust到C的转换
let rust_person = Person {
name: "Alice".to_string(),
age: 30,
};
// 转换为C兼容结构体
let c_person = CReprOf::c_repr_of(&rust_person).unwrap();
// 从C结构体转回Rust
let rust_person2 = Person::as_rust(&c_person).unwrap();
println!("Original: {:?}", rust_person);
println!("Converted back: {:?}", rust_person2);
// 处理字符串转换
let rust_str = "Hello FFI!".to_string();
let c_str = CString::c_repr_of(&rust_str).unwrap();
let rust_str2 = String::as_rust(&c_str).unwrap();
println!("String conversion: {} -> {}", rust_str, rust_str2);
}
完整示例demo
以下是一个更完整的ffi-convert使用示例,展示了结构体、数组和字符串的转换:
use ffi_convert::*;
// 定义Rust结构体
#[derive(Debug, Clone, CReprOf, AsRust)]
#[repr(C)]
struct User {
id: u64,
username: String,
is_active: bool,
permissions: Vec<String>,
}
// C端兼容的结构体
#[repr(C)]
#[derive(Debug)]
struct CUser {
id: u64,
username: *const libc::c_char,
is_active: bool,
permissions: *const *const libc::c_char,
permissions_len: usize,
}
fn main() {
// 创建Rust结构体实例
let rust_user = User {
id: 42,
username: "admin".to_string(),
is_active: true,
permissions: vec!["read".to_string(), "write".to_string()],
};
// 转换为C兼容结构体
let c_user = CReprOf::c_repr_of(&rust_user).unwrap();
// 模拟C端处理
unsafe {
println!("C端收到的数据:");
println!("ID: {}", c_user.id);
println!("Username: {:?}", std::ffi::CStr::from_ptr(c_user.username));
println!("Active: {}", c_user.is_active);
let permissions_slice = std::slice::from_raw_parts(
c_user.permissions,
c_user.permissions_len
);
for &perm_ptr in permissions_slice {
println!("Permission: {:?}", std::ffi::CStr::from_ptr(perm_ptr));
}
}
// 转回Rust结构体
let rust_user2 = User::as_rust(&c_user).unwrap();
println!("\n转换回Rust结构体:");
println!("{:?}", rust_user2);
// 处理字符串数组
let fruits = vec!["apple".to_string(), "banana".to_string()];
let c_fruits = Vec::<*const libc::c_char>::c_repr_of(&fruits).unwrap();
// 转回Rust字符串数组
let fruits2 = Vec::<String>::as_rust(&c_fruits).unwrap();
println!("\n字符串数组转换:");
println!("Original: {:?}", fruits);
println!("Converted back: {:?}", fruits2);
}
主要特性
- 提供
CReprOf
和AsRust
特征,用于双向转换 - 支持常见数据类型(字符串、数组等)的自动转换
#[repr(C)]
兼容的结构体支持- 错误处理机制
许可证
采用以下任一许可证:
- Apache License, Version 2.0
- MIT license
1 回复
Rust FFI转换库ffi-convert使用指南
介绍
ffi-convert
是一个高效的Rust库,专门用于简化Rust与C/C++之间的数据类型转换和跨语言调用。它提供了类型安全的抽象,让开发者能够更轻松地在Rust和其他语言之间传递数据,而无需手动处理繁琐的内存布局和类型转换。
主要特性
- 自动处理Rust与C类型之间的转换
- 支持基本类型、字符串、数组和结构体的转换
- 零成本抽象,转换过程高效
- 类型安全,减少内存错误风险
- 支持双向转换(Rust→C和C→Rust)
安装
在Cargo.toml中添加依赖:
[dependencies]
ffi-convert = "0.4"
基本使用方法
基本类型转换
use ffi_convert::*;
// Rust到C的基本类型转换
let rust_int: i32 = 42;
let c_int = rust_int.to_ffi_value(); // 转换为C兼容的i32
// C到Rust的基本类型转换
let c_int: i32 = 100;
let rust_int = i32::from_ffi_value(c_int); // 转换回Rust的i32
字符串转换
use ffi_convert::*;
// Rust字符串到C字符串
let rust_str = "Hello from Rust!";
let c_string = CString::from(rust_str).unwrap();
// C字符串到Rust字符串
let c_str = unsafe { std::ffi::CStr::from_ptr(c_string.as_ptr()) };
let rust_str = c_str.to_str().unwrap();
结构体转换
use ffi_convert::*;
// 定义Rust结构体
#[derive(AsRust, IntoFfi)]
#[repr(C)]
struct Person {
name: String,
age: i32,
}
// Rust到C的转换
let rust_person = Person {
name: "Alice".to_string(),
age: 30,
};
let c_person = rust_person.into_ffi_value();
// C到Rust的转换
let rust_person_back = Person::from_ffi_value(c_person);
数组转换
use ffi_convert::*;
// Rust Vec到C数组
let rust_vec = vec![1, 2, 3, 4, 5];
let c_array = CDrop::from(rust_vec);
// C数组到Rust Vec
let rust_vec_back = Vec::<i32>::try_from(c_array).unwrap();
高级用法
自定义类型转换
use ffi_convert::*;
#[derive(Debug)]
struct CustomType(i32);
impl IntoFfi for CustomType {
type FfiType = i32;
fn into_ffi_value(self) -> Self::FfiType {
self.0
}
}
impl<'a> FromFfi<'a> for CustomType {
type FfiType = i32;
fn from_ffi_value(value: Self::FfiType) -> Self {
CustomType(value)
}
}
// 使用自定义转换
let custom = CustomType(42);
let c_value = custom.into_ffi_value();
let rust_value = CustomType::from_ffi_value(c_value);
跨语言调用示例
Rust端:
use ffi_convert::*;
#[no_mangle]
pub extern "C" fn process_data(data: *const i32, len: usize) -> *mut CDrop<Vec<i32>> {
// 将C数组转换为Rust Vec
let rust_vec = unsafe { Vec::<i32>::from_raw_parts(data as *mut i32, len, len) };
// 处理数据(示例:每个元素加1)
let processed: Vec<i32> = rust_vec.iter().map(|x| x + 1).collect();
// 转换回C兼容类型并返回
CDrop::into_raw(CDrop::from(processed))
}
C端调用:
#include <stdio.h>
#include <stdint.h>
// 声明Rust函数
extern int32_t* process_data(const int32_t* data, size_t len);
int main() {
int32_t input[] = {1, 2, 3, 4, 5};
size_t len = sizeof(input) / sizeof(input[0]);
// 调用Rust函数
int32_t* result = process_data(input, len);
// 处理结果
for (size_t i = 0; i < len; i++) {
printf("%d ", result[i]);
}
printf("\n");
// 释放内存
free(result);
return 0;
}
完整示例代码
下面是一个完整的Rust与C交互示例,展示了如何使用ffi-convert进行双向数据转换:
Rust库代码(lib.rs):
use ffi_convert::*;
// 定义一个跨语言的结构体
#[derive(AsRust, IntoFfi, Debug)]
#[repr(C)]
struct User {
id: i32,
name: String,
active: bool,
}
// 导出的C接口函数
#[no_mangle]
pub extern "C" fn create_user(id: i32, name: *const libc::c_char, active: bool) -> *mut User {
// 将C字符串转换为Rust字符串
let c_str = unsafe { std::ffi::CStr::from_ptr(name) };
let name_str = c_str.to_str().unwrap().to_string();
// 创建User对象并转换为FFI指针
let user = User {
id,
name: name_str,
active
};
Box::into_raw(Box::new(user))
}
// 导出的C接口函数
#[no_mangle]
pub extern "C" fn print_user(user: *const User) {
// 将指针转换为Rust引用
let user = unsafe { &*user };
println!("User: {:?}", user);
}
// 导出的C接口函数
#[no_mangle]
pub extern "C" fn free_user(user: *mut User) {
// 释放内存
unsafe { Box::from_raw(user) };
}
C调用代码(main.c):
#include <stdio.h>
#include <stdbool.h>
// 声明Rust函数
extern void* create_user(int32_t id, const char* name, bool active);
extern void print_user(const void* user);
extern void free_user(void* user);
int main() {
// 创建一个用户
void* user = create_user(1, "John Doe", true);
// 打印用户信息
print_user(user);
// 释放内存
free_user(user);
return 0;
}
注意事项
- 确保使用
#[repr(C)]
标记需要在Rust和C之间传递的结构体 - 注意内存管理,特别是当Rust和C之间传递所有权时
- 对于复杂类型,可能需要实现自定义的
IntoFfi
和FromFfi
trait - 在跨语言边界传递字符串时,注意编码问题
ffi-convert
通过提供类型安全的抽象层,大大简化了Rust与其他语言交互的复杂性,同时保持了高性能。