如何在自有指针周围公开安全包装?

时间:2015-08-28 21:40:46

标签: 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 {
        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我可以根据它是否是独立的并且需要被释放,或者它是否是参考,我可以有单独的结构,但我希望这是不必要的

2 个答案:

答案 0 :(得分:4)

  

我可以为StructA提供单独的结构,基于它是否是独立的并且需要被释放或者它是否是参考,但我希望这是不必要的。

这是必要的。拥有的StructA *和借用的StructA *之间的差异与Box<T>&T之间的差异完全相同。他们只是一个指针&#34;但是语义完全不同。

这些内容可能是您想要的:

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) }
}

特别值得注意的是,RefA上的生命周期参数可以让编译器确保在释放后备结构后不使用RefA

答案 1 :(得分:2)

  

我可以为StructA提供单独的结构,基于它是否是独立的并且需要被释放或者它是否是参考,但我希望这是不必要的。

我相信这将是公认的模式。对于备份,我指出这是Rust库中的正常模式。 &strString&[T]Vec<T>PathPathBuf以及其他很多我无法想到的其他内容。

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

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();
}