2015-06-27 33 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'方法,也許有些東西需要從它們的實現中學習。我將不得不再次下潛。 –

相關問題