在 RefCell<X> 和 RefCell<&X> 上使用 borrow_mut 的区别

6
如果我理解正确,在 Rust 中无法创建可变借用 over std::rc::Rc,你必须使用 CellRefCell。但无论如何,我不明白如何使用它们。例如,考虑这个简单的例子
use std::cell::RefCell;

struct X (i32);

impl X {
    fn foo(&mut self) {
        self.0 = 0;
    }
}

fn main () {
    let x = X(5);
    let rcx = RefCell::new(&x);

    let mut mutx: std::cell::RefMut<&X> = rcx.borrow_mut();
    (*mutx).foo();
}

我遇到了以下错误:
16:5: 16:9 error: cannot borrow immutable local variable `mutx` as mutable
16     mutx.foo();

但是,如果我从代码行中删除引用(并更新mutx的类型):
let rcx = RefCell::new(x);

一切都很好。但我不明白为什么,因为在第16行调用的解引用 RefMut::deref_mut() -> &mut T 应该在第一种情况下返回 &&mut T,而在第二种情况下返回 &mut T。但由于编译器应该根据需要应用许多 *(如果我理解解引用强制转换的工作方式),所以 RefMut<X>::deref_mut()RefMut<&X>::deref_mut() 之间没有区别。 编辑: 我错误地忘记在第15行写上 mut,就像链接示例中正确地写的那样。所以现在是 let mut mutx...
1个回答

4
问题的根源在于您在RefCell中存储了一个不可变引用。我不清楚为什么您会想要这样做。通常的模式是将整个值放入RefCell中,而不仅仅是一个引用:
fn main () {
    let rcx = RefCell::new(X(5));

    let mut mutx = rcx.borrow_mut();
    mutx.foo();
}

原问题中的问题

你有两个复合错误。让我们检查整个错误信息:

<anon>:16:5: 16:12 error: cannot borrow immutable borrowed content as mutable
<anon>:16     (*mutx).foo();
              ^~~~~~~
<anon>:16:7: 16:11 error: cannot borrow immutable local variable `mutx` as mutable
<anon>:16     (*mutx).foo();
                ^~~~

注意第二个错误 - "不能借用不可变的本地变量mutx"。这是因为您需要声明mutx变量为可变的:

let mut mutx: std::cell::RefMut<&X> = rcx.borrow_mut();

这将允许 mutx 参与 DerefMut


抱歉,我在链接的代码中放置了mut,但我没有更新帖子中的代码。无论如何(关于第一个问题),即使我使用RefCell :: new(x)(不带引用),我也将一个不可变值存储在RefCell中,但这不会生成任何错误。我在这里使用引用的原因是我的原始代码实际上有点复杂(您可以在此处检查:http://is.gd/XoROad) - Kill KRT
1
"RefCell::new(x)" 存储的不是一个不可变值,这是一个常见的误解。你可能对该值有一个不可变的绑定,但当你转移所有权时,你可以选择拥有什么可变性。详情请参考:http://is.gd/di1UDT。 - Shepmaster
2
@KillKRT 虽然Shepmaster是正确的,但可能需要更清晰。您需要理解的是,值具有固定位置(在堆栈、堆或其他位置)。这些可以是不可变的。但是,当您从一个值移动到另一个值foo = bar时,所包含的内存的所有权被转移。此转移可以是到可变位置,这可以给予传递可变访问权限。但是您不能使用引用来做到这一点,因为您无法从引用中移出,因此永远无法将引用数据移动到可变位置。 - Veedrac
2
@KillKRT RefCell 也有一些特殊之处,因为它允许打破可变性的传递性:一个不可变的 RefCell 可以给你对其内容的可变访问权限。如果其内容是一个值,它可以给出 &mut T。但如果其内容是一个引用,它只能给出 &mut &T,这使您可以修改内部引用指向的位置,但不能修改内部引用所指向的值。 - Veedrac
@Veedrac,感谢你的解释,对我帮助很大。 - Kill KRT

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