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);
}

主要特性

  1. 提供CReprOfAsRust特征,用于双向转换
  2. 支持常见数据类型(字符串、数组等)的自动转换
  3. #[repr(C)]兼容的结构体支持
  4. 错误处理机制

许可证

采用以下任一许可证:

  • 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;
}

注意事项

  1. 确保使用#[repr(C)]标记需要在Rust和C之间传递的结构体
  2. 注意内存管理,特别是当Rust和C之间传递所有权时
  3. 对于复杂类型,可能需要实现自定义的IntoFfiFromFfi trait
  4. 在跨语言边界传递字符串时,注意编码问题

ffi-convert通过提供类型安全的抽象层,大大简化了Rust与其他语言交互的复杂性,同时保持了高性能。

回到顶部