Rust 我怎样才能在拥有的指针周围公开一个安全的包装器?

Rust 我怎样才能在拥有的指针周围公开一个安全的包装器?,rust,Rust,我正在包装一个有两个结构的C库:一个有指向另一个的指针 struct StructA { void * some_mem; }; struct StructB { void * some_mem; struct StructA * some_struct; }; 这两个结构都有内存,所以我的包装器都有构造函数和析构函数 struct StructA(*mut c_void); impl StructA { fn new() -> Self {

我正在包装一个有两个结构的C库:一个有指向另一个的指针

struct StructA {
    void * some_mem;
};

struct StructB {
    void * some_mem;
    struct StructA * some_struct;
};
这两个结构都有内存,所以我的包装器都有构造函数和析构函数

struct StructA(*mut c_void);

impl StructA {
    fn new() -> Self {
        StructA(c_constructor())
    }
}

impl Drop for StructA {
    fn drop(&mut self) {
        let StructA(ptr) = self;
        c_destructor(ptr);
    }
}
还有一个函数,它获取指向
StructB
的指针,并返回指向
StructA
的指针:

const struct StructA * get_struct(const struct StructB * obj);
此函数的用户不应释放返回的指针,因为当用户释放
obj
时,指针将被释放

如何包装此函数?问题是
StructB
的析构函数释放了所有内存,包括
StructA
的内存。因此,如果我对
get_struct
的包装返回一个对象,那么包装的
StructA
将被释放两次(对吗?)。它可以返回对对象的引用,但该对象将位于何处

我可以为
StructA
创建单独的结构,这取决于它是独立的,是否需要释放,或者它是否是引用,但我希望这是不必要的

我可以根据StructA是独立的并且需要释放,或者它是引用,为StructA设置单独的结构,但我希望这是不必要的

这是必要的。拥有的
StructA*
和借用的
StructA*
之间的区别与
&T
之间的区别完全相同。它们都是“指针”,但语义完全不同

沿着这些思路的东西可能就是你想要的:

use std::marker::PhantomData;
struct OwnedA(*mut c_void);
impl Drop for OwnedA {
    fn drop(&mut self) { }
}
impl OwnedA {
    fn deref(&self) -> RefA { RefA(self.0, PhantomData) }
}
struct RefA<'a>(*mut c_void, PhantomData<&'a u8>);

struct OwnedB(*mut c_void);

impl Drop for OwnedB {
    fn drop(&mut self) { }
}

impl OwnedB {
    fn get_a(&self) -> RefA { RefA(get_struct(self.0), PhantomData) }
}
使用std::marker::PhantomData;
结构所有者(*多个c_void);
针对OwnedA的impl Drop{
fn drop(&mut self){}
}
impl OwnedA{
fn deref(&self)->RefA{RefA(self.0,PhantomData)}
}
结构重构);
结构所有者B(*多个c_void);
针对OwnedB的impl Drop{
fn drop(&mut self){}
}
impl-OwnedB{
fn get_a(&self)->RefA{RefA(get_struct(self.0),PhantomData)}
}
特别值得注意的是,RefA上的lifetime参数允许编译器确保在释放支持结构后不使用
RefA

我可以根据StructA是独立的并且需要释放,或者它是引用,为StructA设置单独的结构,但我希望这是不必要的

我相信这将是公认的模式。对于备份,我要指出一个事实,这是Rust库中的正常模式
&str
String
和[T]
Vec
Path
PathBuf
,可能还有很多我想不到的东西

好消息是,您可以使用与这些对类似的模式,利用
Deref
DerefMut
调用共享实现:

use std::ops::{Deref, DerefMut};

enum RawFoo {}

fn c_foo_new() -> *const RawFoo { std::ptr::null() }
fn c_foo_free(_f: *const RawFoo) {}
fn c_foo_count(_f: *const RawFoo) -> u8 { 42 }
fn c_foo_make_awesome(_f: *const RawFoo, _v: bool) { }

struct OwnedFoo(Foo);

impl OwnedFoo {
    fn new() -> OwnedFoo {
        OwnedFoo(Foo(c_foo_new()))
    }
}

impl Drop for OwnedFoo {
    fn drop(&mut self) { c_foo_free((self.0).0) }
}

impl Deref for OwnedFoo {
    type Target = Foo;
    fn deref(&self) -> &Self::Target { &self.0 }
}

impl DerefMut for OwnedFoo {
    fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
}

struct Foo(*const RawFoo);

impl Foo {
    fn count(&self) -> u8 { c_foo_count(self.0) }
    fn make_awesome(&mut self, v: bool) { c_foo_make_awesome(self.0, v) }
}

fn main() {
    let mut f = OwnedFoo::new();
    println!("{}", f.count());
    f.make_awesome(true);
}
然后,当您从另一个对象获得借用的指针时,只需将其包装在
&Foo

use std::mem;

fn c_bar_foo_ref() -> *const RawFoo { std::ptr::null() }

// Ignoring boilerplate for wrapping the raw Bar pointer 
struct Bar;

impl Bar {
    fn new() -> Bar { Bar }

    fn foo(&self) -> &Foo {
        unsafe { mem::transmute(c_bar_foo_ref()) }
    }

    fn foo_mut(&mut self) -> &mut Foo {
        unsafe { mem::transmute(c_bar_foo_ref()) }
    }
}

fn main() {
    let mut b = Bar::new();
    println!("{}", b.foo().count());
    b.foo_mut().make_awesome(true);

    // Doesn't work - lifetime constrained to Bar
    // let nope = Bar::new().foo();
}