我有一个c库,它的接口类似于这样:(我在rust中表示了c api,所以这个问题中的所有代码都可以在一个
.rs
文件和易于测试)
struct c_A {}
struct c_B {}
extern "C" fn create_a() -> *mut c_A {
let a = Box::new(c_A {});
Box::into_raw(a)
}
extern "C" fn destroy_a(a: *mut c_A) {
let _a: Box<c_A> = unsafe { Box::from_raw(a) };
}
extern "C" fn create_b(_a: *mut c_A) -> *mut c_B {
let b = Box::new(c_B {});
Box::into_raw(b)
}
extern "C" fn destroy_b(_a: *mut c_A, b: *mut c_B) {
let _b = unsafe { Box::from_raw(b) };
}
我在c函数上创建了以下生锈的抽象:
struct A {
a_ptr: *mut c_A,
}
impl A {
fn new() -> A {
A { a_ptr: create_a() }
}
}
impl Drop for A {
fn drop(&mut self) {
destroy_a(self.a_ptr);
}
}
struct B<'a> {
b_ptr: *mut c_B,
a: &'a A,
}
impl<'a> B<'a> {
fn new(a: &'a A) -> B {
B {
b_ptr: create_b(a.a_ptr),
a,
}
}
}
impl<'a> Drop for B<'a> {
fn drop(&mut self) {
destroy_b(self.a.a_ptr, self.b_ptr);
}
}
这个
B
结构包含对
A
唯一的原因是
a_ptr
打电话给
destroy_b
用于内存清理的函数。我不需要这个参考资料来查我的任何生锈代码。
我现在要创建以下同时引用a和b的结构:
struct C<'b> {
a: A,
b: B<'b>,
}
impl<'b> C<'b> {
fn new() -> C<'b> {
let a = A::new();
let b = B::new(&a);
C { a, b }
}
}
// Main function just so it compiles
fn main() {
let c = C::new();
}
但是,这不会编译:
error[E0597]: `a` does not live long enough
--> src/main.rs:76:25
|
76 | let b = B::new(&a);
| ^ borrowed value does not live long enough
77 | C { a, b }
78 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the lifetime 'b as defined on the impl at 73:1...
--> src/main.rs:73:1
|
73 | impl<'b> C<'b> {
| ^^^^^^^^^^^^^^
我明白为什么失败了:当返回
C
结构自
C::new()
,它移动
C类
是的。这意味着
一个
包含在中的将被移动,这将使对它的所有引用无效。因此,我无法创造
C类
结构。(
Explained in much more detail here
)
如何重构此代码,以便能够存储
乙
在结构及其“父”中
一个
是吗?我想了几个办法,都不行:
-
更改C接口:我不控制C接口,因此无法更改它。
-
拥有
乙
存储A
*mut c_A
而不是
&A
:如果
一个
被删除,则原始指针将变为无效,并在以下情况下导致未定义的行为
乙
被丢弃。
-
拥有
乙
存储所拥有的
一个
而不是参考
&一个
:对于我的用例,我需要能够创建多个
乙
S代表每个
一个
. 如果
乙
拥有
一个
,则每个
一个
只能用于创建一个
乙
.
-
拥有
一个
拥有的所有实例
乙
,并且只返回对
乙
创建新的
乙
:问题是
乙
s将随着时间的推移而累积,直到
一个
被丢弃,用光了多余的内存。不过,如果这确实是最好的办法,我可以处理轻微的不便。
-
使用
rental
我宁愿接受轻微的内存使用命中,也不把新宏的复杂性添加到我的代码中。(即,阅读我的代码的人的复杂性,需要了解这个宏是如何工作的)
我怀疑最好的解决方案至少包括存储
一个
所以不需要移动,但我不知道怎么做。此外,我想知道是否有什么聪明的事情,我可以做使用原始指针。