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.or 3.,为什么它编译得很好?被调用的函数 in1.并没有做任何与2.or完全不同的事情3.。那么为什么1.编译失败呢?

尽管有许多类似的标题问题,但我无法清楚地将其识别为欺骗(除了错误消息相同),可能是因为我对 Rust 中的所有权/借用系统缺乏了解。

4

1 回答 1

12

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

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

这个函数表明所有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);
}

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

于 2015-06-27T21:55:43.133 回答