如何使用 RefCell::borrowmut 借用对结构体不同字段的可变引用

Har*_*rry 6 rust

如何获取对存储在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)

Kev*_*eid 8

编译器理解将单个借用拆分为其字段的借用。然而,有一个看不见的原因导致这在这里不起作用:

\n
let mut a_obj = a_ref_cell.borrow_mut();\nlet a_ref = &mut a_obj.a;\nlet b_ref = &mut a_obj.b;\n
Run Code Online (Sandbox Code Playgroud)\n

a_obj不是可变引用,而是一个std::cell::RefMut保护对象,它在删除时释放借用,并在此之前通过实现提供对内容的访问DerefMut。因为DerefMut::deref_mut()原则上是任意 Rust 代码,所以它的处理方式与任何函数 \xe2\x80\x94\xc2\xa0 严格相同,您fn(&mut self) -> &mut SomethingElse必须在再次调用它之前删除借用。

\n

这个问题的简单本地解决方案适用于任何类型的可解引用事物,即在处理其字段之前借用一次守卫的内容。(我添加了类型注释以进行解释;它们不是必需的。)

\n
let mut a_obj: RefMut<\'_, A> = a_ref_cell.borrow_mut();\nlet a_obj: &mut A = &mut *a_obj;\n
Run Code Online (Sandbox Code Playgroud)\n

现在您可以单独借用字段a_obj。这种借用也可以以同样的方式完成let(这可以通过应用于结果的临时生命周期延长来实现)borrow_mut()来允许的):

\n
let a_obj = &mut *a_ref_cell.borrow_mut();\n
Run Code Online (Sandbox Code Playgroud)\n


FZs*_*FZs 5

这没有什么坏处,而且这确实受到可变引用的支持(称为借用拆分)。

但是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)