2016-10-03 83 views
11

我正在與借閱檢查員爭鬥。我有兩個類似的代碼片斷,其中一個按我期望的工作,另一個不工作。誰借了一個變量?

的一個,因爲我期待的作品:

mod case1 { 
    struct Foo {} 

    struct Bar1 { 
     x: Foo, 
    } 

    impl Bar1 { 
     fn f<'a>(&'a mut self) -> &'a Foo { 
      &self.x 
     } 
    } 

    // only for example 
    fn f1() { 
     let mut bar = Bar1 { x: Foo {} }; 
     let y = bar.f(); // (1) 'bar' is borrowed by 'y' 
     let z = bar.f(); // error (as expected) : cannot borrow `bar` as mutable more 
          // than once at a time [E0499] 
    } 

    fn f2() { 
     let mut bar = Bar1 { x: Foo {} }; 
     bar.f(); // (2) 'bar' is not borrowed after the call 
     let z = bar.f(); // ok (as expected) 
    } 
} 

的一個不:

mod case2 { 
    struct Foo {} 

    struct Bar2<'b> { 
     x: &'b Foo, 
    } 

    impl<'b> Bar2<'b> { 
     fn f(&'b mut self) -> &'b Foo { 
      self.x 
     } 
    } 

    fn f4() { 
     let foo = Foo {}; 
     let mut bar2 = Bar2 { x: &foo }; 
     bar2.f(); // (3) 'bar2' is borrowed as mutable, but who borrowed it? 
     let z = bar2.f(); // error: cannot borrow `bar2` as mutable more than once at a time [E0499] 
    } 
} 

我希望我可以打電話給Bar2::f兩次無刺激性的編譯器,如情況1。

問題是在評論(3):誰借了bar2,而沒有任何作用?

這裏是我的理解:

  1. 在案例1中,f2電話:壽命參數'a是接收&Foo值的一個,所以這一輩子是空的時,有沒有做作,和bar是撥打Bar1::f後不會借用;

  2. 在情況2中,bar2借用foo(爲不可改變),所以壽命參數'bBar2結構是foo壽命基準時間,其在f4本體的端部終止。調用Bar2::f借用bar2該生存期,即到f4的末尾。

但問題仍然是:誰借了bar2?難道是Bar2::f?通話結束後Bar2::f如何保留借來的所有權?我在這裏錯過了什麼?

我在x86_64-pc-windows-msvc上使用Rust 1.14.0-nightly(86affcdf6 2016-09-28)。

回答

7

啊......你基本上是自我借用自己。

問題取決於您的生命週期('b)同時用於Foo的整個生命週期和Bar的生命週期。然後,編譯器會忠實地統一這些生命週期,最終會出現一種奇怪的情況,那就是在該語句結束時應該結束的借用期限在應該超出範圍之後結束。

作爲一條經驗法則:總是使用self的新生命。其他任何東西都很奇怪。


很有趣的是,這種模式實際上是有用的(儘管很可能有一個不變的借位):它允許錨固值到堆棧幀,防止調用之後的任何舉動的功能,它有時可以用來表示一個不適合Rust的借用(例如將一個指針傳遞給FFI的值)。

+0

接受因爲這個有價值的經驗法則。謝謝。 – jferard

1

我把一個main()f4()身體和實施DropBar2找出當它被丟棄(即超出範圍):

impl<'b> Drop for Bar2<'b> { 
    fn drop(&mut self) { println!("dropping Bar2!"); } 
} 

,結果是:

error: `bar2` does not live long enough 
    --> <anon>:24:5 
    | 
24 |  bar2.f(); 
    |  ^^^^ does not live long enough 
25 | } 
    | - borrowed value dropped before borrower 
    | 
    = note: values in a scope are dropped in the opposite order they are created 

有點腥;讓我們來看看它的細節,以幫助範圍:

fn main() { 
    { 
     let foo = Foo {}; // foo scope begins 
     { 
      let mut bar2 = Bar2 { x: &foo }; // bar2 scope begins; bar2 borrows foo 
      bar2.f(); 
     } // bar2 should be dropped here, but it has the same lifetime as foo, which is still live 
    } // foo is dropped (its scope ends) 
} 

它看起來對我說,這裏有一個泄漏,bar2是永不掉線(因此Drop無法實現它)。這就是爲什麼你不能重新借用它。

8

在情況#2,你有這樣的:

impl<'b> Bar2<'b> { 
    fn f(&'b mut self) -> &'b Foo { 
     self.x 
    } 
} 

爲了突出:&'b mut self&'b Foo配置相同的壽命。

這就是說,對self的引用和對Foo實例的返回引用都具有相同的生命週期。縱觀調用點,你有這樣的:

let foo = Foo {}; 
let mut bar2 = Bar2 { x: &foo }; 

所以編譯器推斷,這兩個foobar2有相同的壽命。 foo的生存期是f4函數的範圍,因此對bar2的可變引用將共享此函數。解決這個問題

的一種方式,是去除對self參考明確的一生:

fn f(&mut self) -> &'b Foo 

這編譯和編譯器能夠正確理解,提及bar2和參考foo有不同的壽命。

遊樂場:https://play.rust-lang.org/?gist=caf262dd628cf14cc2884a3af842276a&version=stable&backtrace=0

TLDR:是的,具有對自參考和返回參考相同的壽命說明符意味着f4整個範圍保持的bar2一個可變的借位。