为什么 RefCell 不具有与常规引用相同的范围?

问题描述 投票:0回答:1

我可以编写以下函数

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
而不仅仅是常规引用,则会出现编译时错误,即使逻辑在其他方面(看似)相同:

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
,因此在运行时进行检查,但如果是这种情况,有什么方法可以“签入” ' 我借用的价值,因为它不再需要了?或者是我让它超出范围的唯一选择? (对于这个小例子来说,这很好,但对于较大的例子来说,在审美上令人不快。)

rust scope
1个回答
0
投票

我的猜测是编译器无法确定我是否完成了data

编译器可以,但它选择不这样做,以避免更糟糕的意外。这里的具体规则是

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

删除时间/顺序很重要,因为它可能会产生副作用,因此编译器会使其保持可预测性。

有什么方法可以“签入”我借用的价值,因为它不再需要了?

明确放弃它。像这样改变你的

match
,它将编译:

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

现在编译器发现

data
不再存在,并且不再借用
self.data
,因此它允许您在之后专门借用
self.data

© www.soinside.com 2019 - 2024. All rights reserved.