如何从Rc<RefCell<A>>中获取&A引用?

11

我有一个设计问题,希望用安全的Rust解决,但我还没有找到可行的解决方案。我不能使用RefCell,因为你只能得到Ref/RefMut而不是数据的&引用。

这里是一个简化的示例,已删除不相关的字段/方法

use std::cell::RefCell;
use std::rc::Rc;

struct LibraryStruct {}
impl LibraryStruct {
    fn function(&self, _a: &TraitFromLibrary) {}
}

trait TraitFromLibrary {
    fn trait_function(&self, library_struct: LibraryStruct);
}

// I don't want to copy this, bad performance
struct A {
    // fields...
}

impl TraitFromLibrary for A {
    fn trait_function(&self, library_struct: LibraryStruct) {
        // custom A stuff
    }
}

// B manipulates A's in data
struct B {
    data: Vec<A>,
}

struct C {
    // This type doesn't have to be & for solution. C just needs immutable access
    a: Rc<RefCell<A>>,
}

impl<'a> TraitFromLibrary for C {
    fn trait_function(&self, library_struct: LibraryStruct) {
        // custom C stuff

        // Takes generic reference &, this is why Ref / RefCell doesn't work
        library_struct.function(&self.a.borrow());
    }
}

// B and C's constructed in Container and lifetime matches Container
// Container manipulates fields b and c
struct Container {
    b: B,
    c: Vec<C>,
}

fn main() {}

我可以使用Rc<RefCell<A>>来解决这个问题,但是我的代码库要求使用&A

这会产生以下错误:

error[E0277]: the trait bound `std::cell::Ref<'_, A>: TraitFromLibrary` is not satisfied
  --> src/main.rs:33:33
   |
33 |         library_struct.function(&self.a.borrow());
   |                                 ^^^^^^^^^^^^^^^^ the trait `TraitFromLibrary` is not implemented for `std::cell::Ref<'_, A>`
   |
   = note: required for the cast to the object type `TraitFromLibrary`

我试图对C的字段类型保持模糊,因为它不能是&'a A。结构体B和C同时存在于容器中,这会导致可变性问题,因为在C中存在一个不可变引用,而B有时需要可变性。 - Jake
好的,理想情况下你希望 Ca 是一个 RefCell,但这在库函数中行不通? - Peter Hall
是的,因为您无法从RefCell获取&A,只能获取Ref<A> / RefMut<A>。 - Jake
我刚刚做了一些更改,我认为这些更改可以传达你的意思。我认为答案就是使用 Rc<RefCell<A>>,然后将其作为 library_struct.function(&self.a.borrow()) 传递给库函数。 - Peter Hall
之所以能够奏效,是因为Ref都实现了Deref接口。如果一个函数接受一个&引用,则您可以传递任何实现Deref的类型的引用,例如这个示例 - Peter Hall
1个回答

8
如果一个函数有一个类型为&A的参数,那么你可以使用任何解引用为A的类型的引用来调用它,包括像&Ref<A>这样的类型。一个类型解引用为另一个类型的概念由Deref特性所捕获。这也是为什么接受&str的函数可以被&String调用(String: Deref<Target = str>)的原因。
因此,如果将a保持为Rc<RefCell<A>>,则可以很容易地通过以下方式修复代码:
library_struct.function(&*self.a.borrow());

请注意,此处对 A 进行了解引用并进行了可借用再借用,以便将其强制转换为特质对象。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接