我正在包装一个有两个结构的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
我可以根据它是否是独立的并且需要被释放,或者它是否是参考,我可以有单独的结构,但我希望这是不必要的
答案 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库中的正常模式。 &str
和String
,&[T]
和Vec<T>
,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();
}