不能借用 `self.x` 作为不可变的,因为 `*self` 也借用为可变的

2024-01-09

首先让代码说话:

#[derive(Debug)]
struct Bar;

#[derive(Debug)]
struct Qux {
    baz: bool
}

#[derive(Debug)]
struct Foo {
    bars: Vec<Bar>,
    qux: Qux,
}

impl Foo {
    fn get_qux(&mut self) -> &mut Qux {
        &mut self.qux
    }

    fn run(&mut self) {
        // 1. Fails:
        let mut qux = self.get_qux();

        // 2. Works:
        // let mut qux = &mut Qux { baz: false };

        // 3. Works:
        // let mut qux = &mut self.qux;

        let qux_mut = &mut qux;
        qux_mut.baz = true;

        for bar in &self.bars {
            println!("{:?}", bar);
        }
    }
}

fn main() {
    println!("Hello, world!");

    let mut foo = Foo { bars: vec!(), qux: Qux { baz: false } };
    foo.run();
}

这个错误:

error[E0502]: cannot borrow `self.bars` as immutable because `*self` is also borrowed as mutable
  --> src/main.rs:33:21
   |
22 |         let mut qux = self.get_qux();
   |                       ---- mutable borrow occurs here
...
33 |         for bar in &self.bars {
   |                     ^^^^^^^^^ immutable borrow occurs here
...
36 |     }
   |     - mutable borrow ends here

如果我取消注释2. or 3.,为什么编译得很好?被调用的函数在1.没有做任何与2. or 3.。那么为什么会这样1.编译失败?

虽然有许多类似的标题问题 https://stackoverflow.com/search?q=cannot%20borrow%20as%20immutable,我无法清楚地识别出这是一个骗局(除了错误消息是相同的),可能是因为我对 Rust 中的所有权/借用系统缺乏了解。


在 Rust 中,编译器在评估泛型参数(包括泛型生命周期参数)时会在函数调用边界处停止。在您的情况 1 中,您正在调用一个方法:

fn get_qux(&mut self) -> &mut Qux {
    &mut self.qux
}

该函数表明all of self将被可变地借用,并且返回的引用将一直存在self将要。在此期间,不得对 self 或其组件进行其他借用(可变或不变)。

在你的第二种情况下,你组成了一个全新的Qux与您的结构没有任何附件。这不是一个很好的例子,因为它具有非常不同的含义。如果这个案例适合你,你应该这样做。但是,您不会修改与情况 1 相同的内容。

在第三种情况下,您可以避免函数调用。这意味着编译器有更多关于到底借用了什么的信息。具体可以看出self.qux根本不与self.bars,所以没有错误。

您可以通过添加新范围来使原始示例正常工作:

fn run(&mut self) {
    {
        let mut qux = self.get_qux();
        let qux_mut = &mut qux;
        qux_mut.baz = true;
    }

    for bar in &self.bars {
        println!("{:?}", bar);
    }
}

在这里,人为范围清楚地定义了可变借用的结束位置。一旦借用结束,其他项目就可以进行新的借用。

如果您需要修改qux在循环内部,那么您需要遵循第三种模式:

let mut qux = &mut self.qux;

for bar in &self.bars {
    qux.baz = ! qux.baz;
    println!("{:?}", bar);
}

或者更简单:

for bar in &self.bars {
    self.qux.baz = ! self.qux.baz;
    println!("{:?}", bar);
}

很多时候,您可以重构代码来创建包含信息的新结构,并封装一个很好的突变边界来制作这样的代码。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

不能借用 `self.x` 作为不可变的,因为 `*self` 也借用为可变的 的相关文章

随机推荐