rust - 尝试从包含在 RefCell 中的结构中借用 2 个字段时出错

标签 rust

我有一个包含数据和最终将用于写入数据的写入器的结构。该结构包装在 RefCell 中。这是一个小复制品:

use std::cell::RefCell;
use std::io::Write;

struct Data {
    string: String,
}

struct S {
    data: Data,
    writer: Vec<u8>,
}

fn write(s: RefCell<S>) {
    let mut mut_s = s.borrow_mut();
    let str = &mut_s.data.string;
    mut_s.writer.write(str.as_bytes());
}

编译器生气了:

error[E0502]: cannot borrow `mut_s` as mutable because it is also borrowed as immutable
  --> src\main.rs:16:5
   |
15 |     let str = &mut_s.data.string;
   |                ----- immutable borrow occurs here
16 |     mut_s.writer.write(str.as_bytes());
   |     ^^^^^ mutable borrow occurs here
17 | }
   | - immutable borrow ends here

我应该使用不同的 API 吗?

最佳答案

您可以手动调用 DerefMut,然后保存生成的引用:

fn write(s: RefCell<S>) {
    let mut mut_s = s.borrow_mut();
    let mut tmp = &mut *mut_s; // Here
    let str = &tmp.data.string;
    tmp.writer.write(str.as_bytes());
}

或者在一行中:

fn write(s: RefCell<S>) {
    let mut_s = &mut *s.borrow_mut(); // Here
    let str = &mut_s.data.string;
    mut_s.writer.write(str.as_bytes());
}

问题是 borrow_mut不直接返回你的结构——它返回一个 RefMut .通常,这是透明的,因为此结构实现了 DerefDerefMut ,因此调用它的任何方法都将传递给基础类型。伪扩展代码看起来像这样:

use std::cell::RefMut;
use std::ops::{Deref, DerefMut};

fn write(s: RefCell<S>) {
    let mut mut_s: RefMut<S> = s.borrow_mut();
    let str = &Deref::deref(&mut_s).data.string;
    DerefMut::deref_mut(&mut mut_s).writer.write(str.as_bytes());
}

Rust 不跟踪跨函数调用的字段级借用(即使是 Deref::derefDerefMut::deref_mut)。这会导致您的错误,因为在从先前的 Deref::deref 借用未完成的过程中需要调用 deref_mut 方法。

具有显式借用的扩展版本如下所示,只需调用一次 Deref::deref_mut:

use std::cell::RefMut;
use std::ops::DerefMut;

fn write(s: RefCell<S>) {
    let mut mut_s: RefMut<S> = s.borrow_mut();
    let tmp: &mut S = DerefMut::deref_mut(&mut mut_s);
    let str = &tmp.data.string;
    tmp.writer.write(str.as_bytes());
}

然后,编译器可以跟踪从该临时值借用的两次是不相交的。


请注意,此问题并非RefCell 所独有!任何实现 DerefMut 的类型可以遇到同样的问题。以下是标准库中的一些内容:

关于rust - 尝试从包含在 RefCell 中的结构中借用 2 个字段时出错,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58842924/

相关文章:

rust - 终身省略是否适用于特质隐含方法?

json - 从 serde_json 中的非类型化 JSON 中提取数据时如何处理错误?

rust - 有没有办法对多个语句使用 cfg(feature) 检查?

reference - 在创建别名可变借用之后但在使用之前使用不可变借用实际上很危险吗?

variables - 如何将变量用作不同变量的数据类型?

rust - 进行不安全转换时如何限制生命周期

rust - 了解 rust 借用和取消引用

process - 无法通过管道多次传入或传出生成的子进程

rust - 不安全代码中可变引用的别名是否正确?

build - 如何从构建脚本 (build.rs) 访问当前的 cargo 配置文件(调试/发布,...)