在 FFI 中删除存储的 Rust 空指针

3
我正在封装一个C API,可以通过函数调用设置/获取任意指针。这样,C API允许调用者将任意数据与其中一个C API对象相关联。该数据不会在任何回调中使用,它只是一个用户可以储藏并稍后获取的指针。

我的包装结构为包含此指针的C对象实现了Drop trait。我想要能够做到的是,在包装结构删除时,如果指针不为空,则正确地丢弃数据。然而,我不确定是否可能从原始c_void指针中恢复正确的类型。

我考虑的两种替代方法是:

  1. 在包装器中实现这两个调用的行为。不要对C API进行任何调用。
  2. 不尝试提供任何更安全的接口以应对这些函数。文档说明指针必须由包装器的调用者管理。

我想要做的事情是否可能?如果不可能,这种情况是否有通常被接受的做法?

2个回答

3

以下原因导致单纯的、完全自动化的方法是不可行的:

  • 释放内存时没有调用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)指针,在释放时调用。您也可以将其设置为可选项。


我在一个小时前就想到了dealloc回调函数的解决方案,但不知怎么忘记了。我会尝试着实现一下并看看效果如何。 - user1096614

2

虽然我能够遵循此主题中的建议,但我对我的结果并不完全满意,因此我在 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);
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接