2015-06-27 31 views
12

首先,让代码说话:不能借用`self.x`为永恒不变的,因为`* self`也借为可变

#[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的所有权/借用系统缺乏了解。

回答

8

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

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

该函数表示所有的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); 
} 

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

+3

谢谢您的详细解答。然而,我想知道,如果必须的话,如何在for循环中使用'qux'? –

+0

@FelixSchlitter已更新。 – Shepmaster

+1

我明白了。但是让我们假设该函数有一些额外的逻辑,例如它需要一个id并从quux的vec中检索到'qux',然后我需要在任何地方添加该逻辑。也许我需要诉诸宏观? ...我知道'HashMap'和'Vec'类型有一个'get_mut'方法,也许有些东西需要从它们的实现中学习。我将不得不再次下潜。 –

相关问题