【问题标题】:Mutating fields of Rc Refcell depending on its other internal fieldsRc Refcell 的变异字段取决于其其他内部字段
【发布时间】:2021-04-09 15:12:07
【问题描述】:

我需要遍历 Rc RefCell 中结构的一个字段,并根据它的其他字段修改它的一些参数。 例如对于 struct Foo:

pub struct Foo {
    pub foo1: Vec<bool>,
    pub foo2: Vec<i32>,
}

以下代码恐慌:

fn main() {
    let foo_cell = Rc::new(RefCell::new(Foo { foo1: vec![true, false], foo2: vec![1, 2] }));
    foo_cell.borrow_mut().foo2.iter_mut().enumerate().for_each(|(idx, foo2)| {
        if foo_cell.borrow().foo1[idx] {
            *foo2 *= -1;
        }
    });
}

我可以通过在可变借用之前克隆 foo1 并仅在闭包内使用克隆来解决它,但这会导致不必要的复制。事实上,我基本上需要一个对 foo2 的可变引用和一个对 foo1 的不可变引用(请注意,这是简化的代码,此处无法压缩迭代器)。我知道代码不符合借用规则。有没有办法在不克隆数据的情况下让它工作?

【问题讨论】:

标签: rust borrow-checker rc refcell


【解决方案1】:

问题是您试图在每次循环迭代中重新借用RefCell,而它已经被借用以使迭代器超过foo2。解决方案是在循环之前使用单个 foo_cell.borrow_mut() 并分别借用 foo1 和 foo2。

请注意,需要取消对 foo_cell.borrow_mut() 的引用才能使其正常工作。

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

pub struct Foo {
    pub foo1: Vec<bool>,
    pub foo2: Vec<i32>,
}
fn main() {
    let foo_cell = Rc::new(RefCell::new(Foo {
        foo1: vec![true, false],
        foo2: vec![1, 2]
        
    }));
    // The dereference is required to get &mut Foo out of the RefMut<Foo>
    let borrow = &mut *foo_cell.borrow_mut();
    let foo1 = &borrow.foo1;

    borrow.foo2.iter_mut().enumerate().for_each(|(idx, foo2)| {
        if foo1[idx] {
            *foo2 *= -1;
        }
    });
}

Playground

【讨论】:

  • 关键部分是显式重借&amp;mut *。 borrow_mut() 的返回值不仅仅是一个引用,它是一个实现Deref 和DerefMut 的RefMut 结构。每次访问结构中的字段时,都会调用deref() 和deref_mut() 方法,这对于借用检查器是不透明的,因此它必须将整个结构标记为借用。一旦您提取了对整个结构的单个可变引用,借用检查器可以看到您正在访问结构的单独字段,因为不再涉及不透明的方法调用。
  • 消除变量赋值和内联两个借用 play.rust-lang.org/… 是行不通的,所以我猜借用检查器实际上需要一些额外的帮助才能在闭包之外分配 let foo1 = &amp;borrow.foo1;。可变借用实际上可以内联为borrow.foo2.iter_mut()...。
  • 感谢您的解释,这很有效。我有点希望 Ref 和 RefMut 像常规引用一样工作,但我想情况并非如此
  • 由于闭包中变量捕获的工作方式,您不能内联两个借用。在闭包内引用&amp;borrow.foo1 会使闭包捕获borrow 而不仅仅是borrow.foo1。有一个accepted RFC 可以在未来的某个时候完成这项工作。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-09-21
  • 2016-12-28
  • 1970-01-01
  • 1970-01-01
  • 2020-06-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多