我有一个C库,其接口类似于:(我已经在Rust中代表了C API,因此这个问题中的所有代码都可以在一个.rs文件中连接并轻松测试)
// Opaque handles to C structs
struct c_A {}
struct c_B {}
// These 2 `create` functions allocate some heap memory and other
// resources, so I have represented this using Boxes.
extern "C" fn create_a() -> *mut c_A {
let a = Box::new(c_A {});
Box::into_raw(a)
}
// This C FFI function frees some memory and other resources,
// so I have emulated that here.
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)
}
// Note: While unused here, the argument `_a` is actually used in
// the C library, so I cannot remove it. (Also, I don't control
// the C interface)
extern "C" fn destroy_b(_a: *mut c_A, b: *mut c_B) {
let _b = unsafe { Box::from_raw(b) };
}
Run Code Online (Sandbox Code Playgroud)
我在C函数上创建了以下Rusty抽象:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
该B结构包含一个引用,A唯一的原因a_ptr是在调用该destroy_b函数进行内存清理时是必需的.对于我的任何Rust代码,我都不需要此引用.
我现在想创建以下引用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();
}
Run Code Online (Sandbox Code Playgroud)
但是,这不会编译:
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> {
| ^^^^^^^^^^^^^^
Run Code Online (Sandbox Code Playgroud)
我理解为什么会失败:当C从中返回结构时C::new(),它会移动C.这意味着A移动了包含的内部,这使得对它的所有引用都无效.因此,我无法创建该C结构.(这里详细解释)
我怎样才能以这样一种方式重构这段代码:我可以将B一个结构与它的"父"一起存储在一个结构中A?我想到了一些选项,这是行不通的:
B存储*mut c_A的替代&A:如果A被丢弃,则该生指针变为无效,并且将导致不确定的行为时,B将被丢弃.BStore中拥有的A,而不是一个参考&A:对于我的使用情况下,我需要能够创建多个B了每一个S- A.如果B拥有A,则每个A只能用于创建一个B.A自己的所有实例B,并且仅B在创建新实例时返回引用B:这会产生一个问题,即Bs会随着时间的推移而累积,直到A被删除,占用的内存超过了必要的内存.但是,如果这确实是最好的方法,我可以处理一些轻微的不便.rentalcrate:我宁愿采用轻微的内存使用量,也不愿将新宏的复杂性添加到我的代码中.(也就是说,任何阅读我的代码的人都需要了解这个宏如何工作的复杂性)我怀疑最好的解决方案在某种程度上涉及至少存储在A堆上,因此它不需要移动,但我无法弄清楚如何使这项工作.另外,我想知道我是否可以使用原始指针做一些聪明的事情.
这听起来像是引用计数的理想情况。根据您的多线程需求使用Rc或:Arc
use std::rc::Rc;
struct B {
b_ptr: *mut c_B,
a: Rc<A>,
}
impl B {
fn new(a: Rc<A>) -> B {
B {
b_ptr: create_b(a.a_ptr),
a,
}
}
}
impl Drop for B {
fn drop(&mut self) {
destroy_b(self.a.a_ptr, self.b_ptr);
}
}
fn main() {
let a = Rc::new(A::new());
let x = B::new(a.clone());
let y = B::new(a);
}
Run Code Online (Sandbox Code Playgroud)
A当仍有Bs 引用它时,不能删除它。B可以为每个创建多个s A。A的内存使用量不会永远增加。A及其引用计数。Rc位于标准库中,无需学习新的箱子。将来,您将能够使用任意自身类型以更好的方式编写此代码:
#![feature(arbitrary_self_types)]
use std::rc::Rc;
struct A {
a_ptr: *mut c_A,
}
impl A {
fn new() -> A {
A { a_ptr: create_a() }
}
fn make_b(self: &Rc<Self>) -> B {
B {
b_ptr: create_b(self.a_ptr),
a: self.clone(),
}
}
}
impl Drop for A {
fn drop(&mut self) {
destroy_a(self.a_ptr);
}
}
struct B {
b_ptr: *mut c_B,
a: Rc<A>,
}
impl Drop for B {
fn drop(&mut self) {
destroy_b(self.a.a_ptr, self.b_ptr);
}
}
fn main() {
let a = Rc::new(A::new());
let x = a.make_b();
let y = a.make_b();
}
Run Code Online (Sandbox Code Playgroud)