了解在Rust中如何使用Rc<RefCell<SomeStruct>>

19

我正在查看一些使用的代码

Rc<RefCell<SomeStruct>>

所以我去了解Rc和RefCell之间的区别:

以下是选择Box、Rc或RefCell的原因总结:

Rc允许多个所有者拥有相同的数据;Box和RefCell只允许单个所有者。

Box允许在编译时检查不可变或可变借用;Rc仅允许在编译时检查不可变借用;

RefCell允许在运行时检查不可变或可变借用。因为RefCell允许在运行时检查可变借用,所以即使RefCell是不可变的,也可以修改RefCell内部的值。

因此,Rc确保SomeStruct可以同时被多个人访问。但是怎么访问呢?我只看到了get_mut方法,它返回一个可变引用。但是文本解释说“Rc只允许不可变借用”。

如果可以以可变和不可变的方式访问Rc的对象,那么为什么还需要RefCell呢?


“Rc 只允许不可变借用” - 这句话出自哪里?它并不完全准确,所以我想进行更正。官方文档 只说明 “您通常无法获得对 Rc 内部某些东西的可变引用。” 这个描述已经适当地加上了 “通常”,所以我认为这是可以接受的。 - Sven Marnach
@SvenMarnach 这是来自这里 https://doc.rust-lang.org/book/ch15-05-interior-mutability.html - Guerlando OCs
谢谢。我不确定是否应该更新这个内容。严格来说,你并不一定只能为Rc的内部值获取共享借用,但在大多数实际情况下是这样的,所以让它更正确可能会在其他方面更加困惑。 - Sven Marnach
这个回答解决了你的问题吗?需要关于Rust中cell和引用计数类型的全面解释 - trent
2个回答

26
所以,Rc 确保多个人可以同时访问 SomeStruct。但我该如何访问它呢?
通过解引用。如果您有一个类型为 Rc<...> 的变量 x,则可以使用 *x 访问内部值。在许多情况下,这是隐式发生的;例如,您可以简单地使用 x.method(...) 调用 x 上的方法。
我只看到了 get_mut 方法,它返回一个可变引用。但是文本解释说 "Rc 只允许不可变的借用"。 get_mut() 方法可能比说明 Rc 仅允许不可变借用的时间更近。此外,仅当内部值当前仅有单个所有者时,即如果您目前不需要 Rc,它才会返回可变借用。一旦存在多个所有者,get_mut() 将返回 None
如果可以以可变和不可变的方式访问 Rc 的对象,为什么需要 RefCell 呢?RefCell 可以允许你在存在多个所有者的情况下获得可变的访问权限,即使你只持有一个共享引用到 RefCell。它会动态地在运行时检查,在任何给定时间只存在一个可变引用,并且如果您请求第二个并发的可变引用,它将 panic(或对于 try_borrow 方法返回错误)。这种功能是 Rc 所没有的。
因此,Rc 为您提供了共享所有权。内部值有多个所有者,并且引用计数确保数据保持活动状态,只要至少一个所有者仍然持有它。如果您的数据没有明确的单个所有者,则此功能非常有用。RefCell 为您提供了内部可变性,即您可以在运行时动态地借用内部值,并即使具有共享引用也可以修改它。组合 Rc<RefCell<...>> 给您两者的组合 - 具有多个所有者的值,可以被任何一个所有者可变地借用。
有关更多详细信息,请参阅 Rust 书籍中的相关章节:

1
如果您请求第二个,它会出现错误。小问题:其实不必要,try_borrow[_mut] 已经在将近4年前添加了。 - Masklinn
我已经读了更多。RefCell 允许内部可变性。由于 RefCell 是一个结构体,我可以对其拥有多个不可变引用,但仍然可以通过借用可变来改变其内容。因此,RefCell 允许内部可变性,并且可以传递给任何人。为什么需要 Rc?我认为 Rc 是允许传递给任何人的部分。你能举个例子吗? - Guerlando OCs
2
RefCell 本身只有一个所有者。即使您只有对 RefCell 的共享引用,它也允许您获取对内部值的可变引用。另一方面,Rc 允许共享所有权。指针分配在堆上,并在仍然存在任何引用时保持活动状态。这些概念彼此完全正交,并且通常互补。 - Sven Marnach
1
@AminBashiri 如果没有Rc或类似的智能指针,你就无法实现共享所有权。会有一个单一的所有者,一旦该所有者超出作用域,对象就会被丢弃。 - Sven Marnach
1
@AminBashiri 我认为阅读《Rust编程》这本书中关于所有权的章节会帮助你更好地理解这些概念。 - Sven Marnach
显示剩余4条评论

1
如果在可变和不可变的方式下都可以访问Rc对象,那么为什么还需要RefCell?
- Rc指针允许您拥有共享所有权。由于所有权是共享的,因此Rc指针所拥有的值是不可变的。 - RefCell智能指针表示对其持有的数据的单一所有权,类似于Box智能指针。区别在于,Box智能指针在编译时强制执行借用规则,而RefCell在运行时强制执行借用规则。
如果将它们结合起来,就可以创建一个智能指针,它可以有多个所有者,其中一些所有者可以修改值,而其他所有者则不能。在Rust中创建双向链表是一个完美的使用案例。
struct LinkedList<T>{
    head:Pointer<T>,
    tail:Pointer<T>
}

struct Node<T>{
    element:T,
    next:Pointer<T>,
    prev:Pointer<T>,
}
// we need multiple owners who can mutate the data
// it is Option because "end.next" would be None
type Pointer<T>=Option<Rc<RefCell<Node<T>>>>;

enter image description here

在这张图片中,“front”和“end”节点都会指向“middle”节点,它们都可以改变它。想象一下,如果你需要在“front”后面插入一个新的节点,你需要改变“front.next”。因此,在双向链表中,你需要同时具备多个所有权和可变性能力。

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