C 库释放来自 Rust 的指针
C library freeing a pointer coming from Rust
我想将 Rust 绑定到需要回调的 C 库,并且此回调必须 return 一个指向 C 库的 C 样式 char*
指针,然后释放它。
回调必须在某种意义上暴露给我的库的用户(可能使用闭包),我想提供一个尽可能方便的 Rust 接口(意味着如果可能的话接受 String
输出)。
但是,C 库在尝试 free()
来自 Rust 分配的内存的指针时会报错,可能是因为 Rust 使用 jemalloc 而 C 库使用 malloc。
所以目前我可以看到两个使用 libc::malloc()
的解决方法,但它们都有缺点:
- 给图书馆的用户一个他必须填写的切片(不方便,并且施加长度限制)
- 获取他的
String
输出,将其复制到一个由malloc分配的数组,然后释放String
(无用的复制和分配)
谁能看到更好的解决方案?
这是 C 库接口的等价物,以及理想情况的实现(如果 C 库可以释放在 Rust 中分配的字符串)
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
}
这可能需要您做一些烦人的工作,但是公开一个实现了 Write
但由通过 malloc
分配的内存支持的类型呢?然后,您的客户可以使用 write!
宏(和朋友)而不是分配 String
.
以下是它目前与 Vec
一起使用的方式:
let mut v = Vec::new();
write!(&mut v, "hello, world");
您 "just" 需要实现这两个方法,然后您将拥有一个类似流的接口。
我想将 Rust 绑定到需要回调的 C 库,并且此回调必须 return 一个指向 C 库的 C 样式 char*
指针,然后释放它。
回调必须在某种意义上暴露给我的库的用户(可能使用闭包),我想提供一个尽可能方便的 Rust 接口(意味着如果可能的话接受 String
输出)。
但是,C 库在尝试 free()
来自 Rust 分配的内存的指针时会报错,可能是因为 Rust 使用 jemalloc 而 C 库使用 malloc。
所以目前我可以看到两个使用 libc::malloc()
的解决方法,但它们都有缺点:
- 给图书馆的用户一个他必须填写的切片(不方便,并且施加长度限制)
- 获取他的
String
输出,将其复制到一个由malloc分配的数组,然后释放String
(无用的复制和分配)
谁能看到更好的解决方案?
这是 C 库接口的等价物,以及理想情况的实现(如果 C 库可以释放在 Rust 中分配的字符串)
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
}
这可能需要您做一些烦人的工作,但是公开一个实现了 Write
但由通过 malloc
分配的内存支持的类型呢?然后,您的客户可以使用 write!
宏(和朋友)而不是分配 String
.
以下是它目前与 Vec
一起使用的方式:
let mut v = Vec::new();
write!(&mut v, "hello, world");
您 "just" 需要实现这两个方法,然后您将拥有一个类似流的接口。