同 FFI 交互,主要是在和指针打交道,另外两个有用的工具为:

工具作用

为 Rust 代码导出 C 接口的头文件。

为 C 头文件生成 Rust FFI 声明。

为 Rust 生成 C 接口

指针

首先是两个基本概念:

  • 引用本质上是指针。

  • Rust 中的 Option 本质上是一个 TaggedUnion,但是对于引用(即指针)会执行 空指针优化 。也就是说,空指针视为 None,非空指针视为 Some。

因此对于指针,可以将其写作:

fn set_error(ret: Option<&mut Error>){
    todo!()
}

其效果等同于

fn set_error(ret: *mut Error){
    todo!()
}

函数指针

由于 Rust 中没有函数指针的概念,因此函数总不是空的。因此函数指针总是需要使用 Option:

pub type FnGetSize = extern "C" fn(obj: *const c_void) -> usize;

隐藏字段

当结构体没有使用 repr© 表示且没有创建过对象(只创建过指针)时,cbindgen 只会生成对应结构体的前置类型声明。借助这一方式可以避免 wrap 结构体泄露:

pub struct WrapPerson(usize);

impl WrapPerson {
    #[no_mangle]
    pub extern "C" fn new() -> *mut WrapPerson{
        let obj = Box::new(Person::new());
        let ptr = Box::into_raw(obj);
        ptr as *mut WrapPerson
    }

    #[no_mangle]
    pub extern "C" fn print(&self) {
        let obj = Box::from_raw(core::ptr::addr_of!(*self) as *const Person); (1)
        println!("{obj:?}");
    }
}
  1. 此处不得拷贝 self

正如上述所注,一个常见错误是将 self 进行了拷贝。这回导致 addr_of 得到的是假的地址,从而引发段错误。

包装对象

一个常见的需求是将某个对象包装起来暴露给 C 中,这种情况下可以借助 Box 来进行不透明对象类型包装,例如:

pub struct WrapObj(usize);

impl WrapObj {
    #[no_mangle]
    pub extern "C" fn new() -> *mut Self {
        let obj = Box::new(Obj::default());
        Box::into_raw(obj) as *mut Self
    }
}

这种方式有两点关键:

  • WrapObj 不添加 repr© ,且代码中只通过指针操纵 WrapObj。这样就能避免 WrapObj 被导出。

  • Obj 使用 Box 或者 Arc 创建在堆上,然后又使用 into_raw 和 as 将指针强转成 *mut WrapObj

一般情况下还需要实现 AsRef 和 AsMut 来实现 WrapObj 和 &Obj 之间的方便转换。添加 drop 来释放对象。

堆上对象转栈上对象

Rust 的接口一般使用栈上对象拿取所有权的形式,但是使用 [包装对象] 时所有对象都是堆上对象。这时候如果被包装对象又没有实现 Copy 语义的话,就会导致两边接口不同。这时候可以借助 mem::replace 或者 mem::take 来行使 “移动语义” :

match obj_ptr.is_null() {
    true => Default::default(),
    false => {
        let heap_obj = unsafe {
            &mut *(*obj_ptr as *mut Obj)
        };
        mcore::Obj {
            field1: core::mem::take(&mut heap_obj.field1),
            field2: core::mem::take(&mut heap_obj.field2),
        }
    }
}

这样,复杂类型使用 take 取走所有权,同时原地留下 default(),简单类型直接拷贝即可。

Last moify: 2025-04-07 07:03:40
Build time:2025-07-18 09:41:42
Powered By asphinx