如何获取对存储在RefCell. 获得对结构体不同字段的可变引用有什么危害?
use std::cell::{RefCell, RefMut};
#[derive(Default)]
struct A {
a: i32,
b: u32,
}
fn main() {
let mut a_ref_cell: RefCell<A> = Default::default();
let mut a_obj = a_ref_cell.borrow_mut();
let a_ref = &mut a_obj.a;
let b_ref = &mut a_obj.b;
*a_ref = 2;
*b_ref = 78;
println!("{}, {}", *a_ref, *b_ref);
println!("Hello, world!");
}
Run Code Online (Sandbox Code Playgroud)
错误
error[E0499]: cannot borrow `a_obj` as mutable more than once at a time
--> src/lib.rs:62:22
|
61 | let a_ref = &mut a_obj.a;
| ----- first mutable borrow occurs here
62 | let b_ref = &mut a_obj.b;
| ^^^^^ second mutable borrow occurs here
63 |
64 | *a_ref = 2;
| ---------- first borrow later used here
Run Code Online (Sandbox Code Playgroud)
编译器理解将单个借用拆分为其字段的借用。然而,有一个看不见的原因导致这在这里不起作用:
\nlet mut a_obj = a_ref_cell.borrow_mut();\nlet a_ref = &mut a_obj.a;\nlet b_ref = &mut a_obj.b;\nRun Code Online (Sandbox Code Playgroud)\na_obj不是可变引用,而是一个std::cell::RefMut保护对象,它在删除时释放借用,并在此之前通过实现提供对内容的访问DerefMut。因为DerefMut::deref_mut()原则上是任意 Rust 代码,所以它的处理方式与任何函数 \xe2\x80\x94\xc2\xa0 严格相同,您fn(&mut self) -> &mut SomethingElse必须在再次调用它之前删除借用。
这个问题的简单本地解决方案适用于任何类型的可解引用事物,即在处理其字段之前借用一次守卫的内容。(我添加了类型注释以进行解释;它们不是必需的。)
\nlet mut a_obj: RefMut<\'_, A> = a_ref_cell.borrow_mut();\nlet a_obj: &mut A = &mut *a_obj;\nRun Code Online (Sandbox Code Playgroud)\n现在您可以单独借用字段a_obj。这种借用也可以以同样的方式完成let(这可以通过应用于结果的临时生命周期延长来实现)borrow_mut()来允许的):
let a_obj = &mut *a_ref_cell.borrow_mut();\nRun Code Online (Sandbox Code Playgroud)\n
这没有什么坏处,而且这确实受到可变引用的支持(称为借用拆分)。
但是RefCellsborrow_mut不会返回普通的可变引用:它返回一个RefMut结构(其工作方式类似于可变引用,但它还必须解锁RefCellon Drop)。
它用于DerefMut为您提供可变引用,但这需要借用整个结构而不仅仅是字段。
RefMut有一个函数可以解决这个限制:
let mut a_obj = a_ref_cell.borrow_mut();
let (mut a_obj, mut b_obj) = RefMut::map_split(|a_ref| (&mut a_ref.a, &mut a_ref.b))
*a_obj = 2;
*b_obj = 78;
println!("{}, {}", *a_obj, *b_obj);
Run Code Online (Sandbox Code Playgroud)