C库释放了来自Rust的指针 [英] C library freeing a pointer coming from Rust
问题描述
我想对需要回调的C库进行Rust绑定,并且此回调必须返回指向C库的C样式char*
指针,然后将其释放.
从某种意义上讲,该回调必须向我的库用户公开(可能使用闭包),并且我想提供一个尽可能方便的Rust接口(意味着尽可能接受String
输出).
I want to do Rust bindings to a C library which requires a callback, and this callback must return a C-style char*
pointer to the C library which will then free it.
The callback must be in some sense exposed to the user of my library (probably using closures), and I want to provide a Rust interface as convenient as possible (meaning accepting a String
output if possible).
但是,C库在尝试free()
来自Rust分配的内存的指针时抱怨,这可能是因为Rust使用jemalloc且C库使用malloc.
However, the C library complains when trying to free()
a pointer coming from memory allocated by Rust, probably because Rust uses jemalloc and the C library uses malloc.
所以目前我可以看到使用libc::malloc()
的两种解决方法,但是它们都有缺点:
So currently I can see two workarounds using libc::malloc()
, but both of them have disadvantages:
- 给图书馆用户一个他必须填写的切片(不方便,并施加了长度限制)
- 获取他的
String
输出,将其复制到malloc分配的数组中,然后释放String
(无用的复制和分配)
- Give the user of the library a slice that he must fill (inconvenient, and imposes length restrictions)
- Take his
String
output, copy it to an array allocated by malloc, and then free theString
(useless copy and allocation)
有人可以看到更好的解决方案吗?
Can anybody see a better solution?
这里等效于C库的接口,并且是理想情况的实现(如果C库可以释放Rust中分配的String)
Here is an equivalent of the interface of the C library, and the implementation of the ideal case (if the C library could free a String allocated in 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
}
推荐答案
It might require some annoying work on your part, but what about exposing a type that implements Write
, but is backed by memory allocated via malloc
? Then, your client can use the write!
macro (and friends) instead of allocating a String
.
这是当前与Vec
一起使用的方式:
Here's how it currently works with Vec
:
let mut v = Vec::new();
write!(&mut v, "hello, world");
您将仅需要实现这两种方法,然后您将拥有一个类似于流的接口.
You would "just" need to implement the two methods and then you would have a stream-like interface.
这篇关于C库释放了来自Rust的指针的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!