是否有替代方法或方法让 Rc<RefCell<X>> 限制 X 的可变性?
Posted
技术标签:
【中文标题】是否有替代方法或方法让 Rc<RefCell<X>> 限制 X 的可变性?【英文标题】:Is there an alternative or way to have Rc<RefCell<X>> that restricts mutability of X? 【发布时间】:2019-02-07 10:55:45 【问题描述】:For example given this code:
use std::rc::Rc;
use std::cell::RefCell;
// Don't want to copy for performance reasons
struct LibraryData
// Fields ...
// Creates and mutates data field in methods
struct LibraryStruct
// Only LibraryStruct should have mutable access to this
data: Rc<RefCell<LibraryData>>
impl LibraryStruct
pub fn data(&self) -> Rc<RefCell<LibraryData>>
self.data.clone()
// Receives data field from LibraryStruct.data()
struct A
data: Rc<RefCell<LibraryData>>
impl A
pub fn do_something(&self)
// Do something with self.data immutably
// I want to prevent this because it can break LibraryStruct
// Only LibraryStruct should have mutable access
let data = self.data.borrow_mut();
// Manipulate data
如何防止LibraryData
在LibraryStruct
之外发生变异? LibraryStruct
应该是唯一能够在其方法中改变 data
的方法。 Rc<RefCell<LibraryData>>
可以做到这一点,还是有替代方案?请注意,我正在编写“库”代码,以便可以更改它。
【问题讨论】:
LibraryStruct 中的数据如何提供给 A?我想您不能向 LibraryStruct 添加方法或更改其数据结构? 一个方法返回rc的克隆。请注意,我正在编写“库”代码,并希望尽可能防止出现此问题。 【参考方案1】:如果您共享一个RefCell
,那么它总是有可能对其进行变异——这基本上就是它的全部意义所在。鉴于您能够更改LibraryStruct
的实现,您可以确保data
不公开,并通过getter 方法控制其向用户公开的方式:
pub struct LibraryStruct
// note: not pub
data: Rc<RefCell<LibraryData>>
impl LibraryStruct
// could also have returned `Ref<'a, LibraryData> but this hides your
// implementation better
pub fn data<'a>(&'a self) -> impl Deref<Target = LibraryData> + 'a
self.data.borrow()
在您的其他结构中,您可以保持简单,只需将其视为参考:
pub struct A<'a>
data: &'a LibraryData,
impl<'a> A<'a>
pub fn do_something(&self)
// self.data is only available immutably here because it's just a reference
fn main()
let ld = LibraryData ;
let ls = LibraryStruct data: Rc::new(RefCell::new(ld)) ;
let a = A data: &ls.data() ;
如果您需要更长时间地保留引用,在此期间原始RefCell
需要在库代码中可变地借用,那么您需要制作一个可以管理它的自定义包装器。这可能有一个标准库类型,但我不知道它很容易为您的用例专门制作一些东西:
// Wrapper to manage a RC<RefCell> and make it immutably borrowable
pub struct ReadOnly<T>
// not public
inner: Rc<RefCell<T>>,
impl<T> ReadOnly<T>
pub fn borrow<'a>(&'a self) -> impl Deref<Target = T> + 'a
self.inner.borrow()
现在在你的库代码中返回这个:
impl LibraryStruct
pub fn data<'a>(&'a self) -> ReadOnly<LibraryData>
ReadOnly inner: self.data.clone()
并且当你使用它时,内部的RefCell
将无法直接访问,数据只能用于不可变地借用:
pub struct A
data: ReadOnly<LibraryData>,
impl A
pub fn do_something(&self)
// data is immutable here
let data = self.data.borrow();
fn main()
let ld = LibraryData ;
let ls = LibraryStruct data: Rc::new(RefCell::new(ld)) ;
let a = A data: ls.data() ;
【讨论】:
库结构/数据是可修改的,这仍然是最好的解决方案吗? 如果您可以更改库,并且您不希望在该代码之外对其进行编辑,那么您应该将其保密并防止修改。我只是在更新答案以反映这一点。 我相信第一个解决方案行不通,因为我不能让一个活跃的 Ref 坐在那里,因为它会在 LibraryStruct 修改数据时引起恐慌? 如果你需要借用更长的时间,所以它可以同时变异,那么这将不起作用。您可能需要编写一些只能不可更改地借用的自定义内容。但是你也会遇到 Rc 包装器的问题,因为每个包装器的类型都不同。 我基本上想给出指向 LibraryStruct.data 的指针,这些指针只是不可变的。我可能需要重新设计整个部分的工作方式,似乎违背了生锈的方式。以上是关于是否有替代方法或方法让 Rc<RefCell<X>> 限制 X 的可变性?的主要内容,如果未能解决你的问题,请参考以下文章
Rc<RefCell<T>> 和 RefCell<Rc<T>> 有啥区别?
Rc<RefCell<T>> 和 RefCell<Rc<T>> 有啥区别?
Rayon 如何防止线程之间使用 RefCell<T>、Cell<T> 和 Rc<T>?