【问题标题】:cannot borrow `self.x` as immutable because `*self` is also borrowed as mutable不能将 `self.x` 作为不可变借用,因为 `*self` 也作为可变借用
【发布时间】:2015-09-14 15:03:17
【问题描述】:

首先,让代码说话:

#[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.3.,为什么它编译得很好? 1. 中的被调用函数与2.3. 没有太大的不同。那么为什么1.编译失败呢?

虽然有many similar titled questions,但我无法清楚地将其识别为骗子(除了错误消息相同),可能是因为我对 Rust 中的所有权/借用系统缺乏了解。

【问题讨论】:

    标签: rust immutability mutability


    【解决方案1】:

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

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

    此函数表示self所有 将被可变借用,并且返回的引用将与self 一样长。在此期间,不得对 self 或其组件进行其他借用(可变或非可变)。

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

    在第三种情况下,您避免了函数调用。这意味着编译器有更多关于借用的信息。具体可以看到self.quxself.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);
    }
    

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

    【讨论】:

    • 我明白了。但是假设该函数中有一些额外的逻辑,例如它需要一个 id 并从 Quxes 的 vec 检索qux,然后我需要在任何地方添加该逻辑。也许我需要求助于宏? ...我知道HashMapVec 类型有一个get_mut 方法,也许从它们的实现中也可以学到一些东西。我将不得不再潜水一次。
    • @FelixSchlitter 没有具体的例子(可能是另一个问题),很难给出任何可行的建议。但是,使用共享数据和方法重构结构或接受闭包对我来说都是解决此类问题的有用解决方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-09
    相关资源
    最近更新 更多