是否有替代方法或方法让 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
    

如何防止LibraryDataLibraryStruct 之外发生变异? LibraryStruct 应该是唯一能够在其方法中改变 data 的方法。 Rc&lt;RefCell&lt;LibraryData&gt;&gt; 可以做到这一点,还是有替代方案?请注意,我正在编写“库”代码,以便可以更改它。

【问题讨论】:

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>?

为啥我的 RefCell 的零成本替代方案不是实现内部可变性的标准方法?

如何正确访问 RefCell 中的值

如何将 Rc<RefCell<_>> 中的具体对象转换为动态特征对象? [复制]