首页 文章

C库释放来自Rust的指针

提问于
浏览
3

我想对需要回调的C库执行Rust绑定,并且此回调必须返回一个C风格的 char* 指向C库的指针,然后释放它 . 回调必须在某种意义上暴露给我的库的用户(可能使用闭包),并且我想尽可能方便地提供Rust接口(意味着如果可能的话接受 String 输出) .

但是,C库在尝试使用Rust分配的内存时会抱怨,可能是因为Rust使用了jemalloc而C库使用了malloc .

所以目前我可以看到两个使用 libc::malloc() 的变通方法,但它们都有缺点:

  • 为库的用户提供他必须填写的切片(不方便,并施加长度限制)

  • 拿出他的 String 输出,将其复制到malloc分配的数组中,然后释放 String (无用的复制和分配)

有人能看到更好的解决方案吗?

这是C库的接口的等价物,以及理想情况的实现(如果C库可以释放Rust中分配的String)

extern crate libc;
use std::ffi::CString;
use libc::*;
use std::mem;

extern "C" {
    // The second parameter of this function gets passed as an argument of my callback
    fn need_callback(callback: extern fn(arbitrary_data: *mut c_void) -> *mut c_char,
                     arbitrary_data: *mut c_void);
}

// This function must return a C-style char[] that will be freed by the C library
extern fn my_callback(arbitrary_data: *mut c_void) -> *mut c_char {
    unsafe {
        let mut user_callback: *mut &'static mut FnMut() -> String = mem::transmute(arbitrary_data); //'
        let user_string = (*user_callback)();
        let c_string = CString::new(user_string).unwrap();
        let ret: *mut c_char = mem::transmute(c_string.as_ptr());
        mem::forget(c_string); // To prevent deallocation by Rust
        ret
    }
}

pub fn call_callback(mut user_callback: &mut FnMut() -> String) {
    unsafe {
        need_callback(my_callback, mem::transmute(&mut user_callback));
    }
}

C部分相当于:

#include <stdlib.h>
typedef char* (*callback)(void *arbitrary_data);
void need_callback(callback cb, void *arbitrary_data) {
    char *user_return = cb(arbitrary_data);
    free(user_return); // Complains as the pointer has been allocated with jemalloc
}

1 回答

  • 3

    它可能需要你做一些恼人的工作,但是暴露一个实现Write的类型,但是由通过 malloc 分配的内存支持呢?然后,您的客户端可以使用 write! 宏(和朋友)而不是分配 String .

    以下是它目前如何与 Vec 一起使用:

    let mut v = Vec::new();
    write!(&mut v, "hello, world");
    

    您只需要实现这两种方法,然后就可以使用类似于流的接口 .

相关问题