rust 为什么RefCell没有与常规引用相同的作用域?

dgsult0t  于 2023-10-20  发布在  其他
关注(0)|答案(1)|浏览(109)

我可以写下面的函数f,代码可以像我预期的那样编译:

use std::collections::HashMap;

struct A {
    data: HashMap<u32, B>,
}

struct B {
    val: Option<u32>,
}

impl A {
    fn f(&mut self, key: u32) {
        let data = &self.data[&key];
        match data.val {
            Some(value) => panic!("{}", value),
            None => self.data.remove(&key),
        };
    }
}

在这里,key首先被不可变地检出&self.data[&key],然后再次被可变地检出(self.data.remove(&key)),但是编译器允许这样做,可能是因为在可变检出之后data永远不会被使用。
然而,如果我使用RefCell而不仅仅是一个常规的ref,我会得到一个编译时错误,即使逻辑在其他方面(看起来)是相同的:

use std::collections::HashMap;
use std::cell::RefCell;

struct A {
    data: HashMap<u32, RefCell<B>>,
}

struct B {
    val: Option<u32>,
}

impl A {
    fn f(&mut self, key: u32) {
        let data = self.data[&key].borrow();
        match data.val {
            Some(value) => panic!("{}", value),
            None => self.data.remove(&key),
        };
    }
}

错误是:

error[E0502]: cannot borrow `self.data` as mutable because it is also borrowed as immutable
  --> src/main.rs:17:21
   |
14 |         let data = self.data[&key].borrow();
   |                    --------- immutable borrow occurs here
...
17 |             None => self.data.remove(&key),
   |                     ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
18 |         };
19 |     }
   |     - immutable borrow might be used here, when `data` is dropped and runs the destructor for type `Ref<'_, B>`

我的猜测是,编译器无法确定我完成了data,因为它是一个RefCell,因此在运行时检查,但如果是这样的话,有没有什么方法可以'检入'我借用的值,因为它不再需要?还是我唯一的选择就是让它超出范围?(对于这个小例子来说,这很好,但对于大的例子来说,这在美学上是不合适的。)

whlutmcx

whlutmcx1#

我的猜测是,编译器无法确定我完成了 data
编译器可以,但它选择不这样做,以避免更糟糕的意外。这里的具体规则是,

  • 具有Drop代码的类型的值(例如std::cell::Ref)总是使该代码在作用域的末尾运行 *,
  • 这意味着由这些值持有的任何借用也扩展到范围的末尾。

删除时间/顺序很重要,因为它可能有副作用,所以编译器保持它的可预测性。
有没有什么方法可以'检查'的价值,我借来的,因为它不再需要?
解释一下别说了像这样修改你的match,它将编译:

None => {
    drop(data);
    self.data.remove(&key);
}

现在,编译器发现data不再存在,也不再借用self.data,所以它允许您以后独占地借用self.data

相关问题