我的包装结构为包含此指针的C对象实现了Drop trait。我想要能够做到的是,在包装结构删除时,如果指针不为空,则正确地丢弃数据。然而,我不确定是否可能从原始c_void指针中恢复正确的类型。
我考虑的两种替代方法是:
- 在包装器中实现这两个调用的行为。不要对C API进行任何调用。
- 不尝试提供任何更安全的接口以应对这些函数。文档说明指针必须由包装器的调用者管理。
我想要做的事情是否可能?如果不可能,这种情况是否有通常被接受的做法?
我的包装结构为包含此指针的C对象实现了Drop trait。我想要能够做到的是,在包装结构删除时,如果指针不为空,则正确地丢弃数据。然而,我不确定是否可能从原始c_void指针中恢复正确的类型。
我考虑的两种替代方法是:
我想要做的事情是否可能?如果不可能,这种情况是否有通常被接受的做法?
以下原因导致单纯的、完全自动化的方法是不可行的:
释放内存时没有调用drop/deconstructors等函数:C API可以用于可以拥有需要正确析构的对象的语言,例如C++或Rust本身。因此,当您仅存储内存指针时,您不知道如何调用适当的函数(您既不知道哪个函数也不知道调用约定的样子)。
使用哪个内存分配器?内存分配和释放并非易事。您的程序需要从操作系统请求内存,然后以智能方式管理这些资源以提高效率和正确性。这通常由库完成。在Rust的情况下,使用jemalloc(但可以更改)。因此,即使您要求API调用者仅传递Plain Old Data(应更容易析构),您仍然不知道要调用哪个库函数来释放内存。仅使用libc::free
是行不通的(它可能会失败得很可怕)。
解决方案:
dealloc回调函数:您可以要求API用户设置一个额外的指针,例如void destruct(void* ptr)
函数。如果这个指针不为NULL,在释放时就会调用该函数。您还可以使用int
作为返回类型来表示销毁过程出现错误的情况。在这种情况下,您可以使用panic!
。
全局回调函数:假设您要求用户只传递POD(普通旧数据)。为了知道内存分配器中哪个free
函数需要调用,您可以要求用户注册一个全局void (*free)(void* ptr)
指针,在释放时调用。您也可以将其设置为可选项。
虽然我能够遵循此主题中的建议,但我对我的结果并不完全满意,因此我在 Rust 论坛上提出了问题,并找到了我真正想要的 答案。(
use std::any::Any;
static mut foreign_ptr: *mut () = 0 as *mut ();
unsafe fn api_set_fp(ptr: *mut ()) {
foreign_ptr = ptr;
}
unsafe fn api_get_fp() -> *mut() {
foreign_ptr
}
struct ApiWrapper {}
impl ApiWrapper {
fn set_foreign<T: Any>(&mut self, value: Box<T>) {
self.free_foreign();
unsafe {
let raw = Box::into_raw(Box::new(value as Box<Any>));
api_set_fp(raw as *mut ());
}
}
fn get_foreign_ref<T: Any>(&self) -> Option<&T> {
unsafe {
let raw = api_get_fp() as *const Box<Any>;
if !raw.is_null() {
let b: &Box<Any> = &*raw;
b.downcast_ref()
} else {
None
}
}
}
fn get_foreign_mut<T: Any>(&mut self) -> Option<&mut T> {
unsafe {
let raw = api_get_fp() as *mut Box<Any>;
if !raw.is_null() {
let b: &mut Box<Any> = &mut *raw;
b.downcast_mut()
} else {
None
}
}
}
fn free_foreign(&mut self) {
unsafe {
let raw = api_get_fp() as *mut Box<Any>;
if !raw.is_null() {
Box::from_raw(raw);
}
}
}
}
impl Drop for ApiWrapper {
fn drop(&mut self) {
self.free_foreign();
}
}
struct MyData {
i: i32,
}
impl Drop for MyData {
fn drop(&mut self) {
println!("Dropping MyData with value {}", self.i);
}
}
fn main() {
let p1 = Box::new(MyData {i: 1});
let mut api = ApiWrapper{};
api.set_foreign(p1);
{
let p2 = api.get_foreign_ref::<MyData>().unwrap();
println!("i is {}", p2.i);
}
api.set_foreign(Box::new("Hello!"));
{
let p3 = api.get_foreign_ref::<&'static str>().unwrap();
println!("payload is {}", p3);
}
}