2016-10-03 5 views
11

私は借用チェッカーと戦っています。私は2つの同様のコード片を持っています。一つは私が期待していたもので、もう一つはそうではありません。誰が変数を借りましたか?

私が期待どおりに動作1:ない

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) 
    } 
} 

1:私は、私はケース1のように、コンパイラを刺激せずに二回Bar2::fを呼び出すことができます望ん

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] 
    } 
} 

質問はコメント(3)にあります:誰がbar2を借りたのですが、影響はありませんか?

は、ここで私は理解して何:

ケース1では
  1. f2コール:寿命パラメータ'aは受信&Foo値の一つなので、何も気取りがない場合、この寿命は空で、barがありますBar1::fコールの後に借りていない。ケース2では

  2. は、 bar2foo(不変として)借用し、そう Bar2構造体の寿命パラメータ 'bf4本体の終わりで終了 foo基準寿命です。 Bar2::fを呼び出すと、その生涯、つまり f4の末尾には bar2が借ります。

しかし、質問はまだあります:誰が借りたbar2?それはBar2::fでしょうか?電話後にBar2::fが借用所有権を保持する方法は?私はここで何が欠けていますか?

私はx86_64-pc-windows-msvcでRust 1.14.0-nightly(86affcdf6 2016-09-28)を使用しています。

答えて

7

ああ...あなたは基本的に自己借用しています。

この問題は、寿命がFooであり、寿命がBarで、同じ寿命('b)が使用されているという事実に左右されます。コンパイラはこれらの生存期間を忠実に統一し、突然借り手の生涯が価値の範囲を超えた後に終了するはずの奇妙な状況に陥ります。

経験則として、常にselfの新しい寿命を使います。それ以外は変です。


これは、このパターンは、実際に(不変ボローでの可能性が高いが)役立つことができることを注意することは興味深いです:それはは、スタックフレームに値を固定関数を呼び出した後、任意の移動を防止することができ、これはRustによってうまくモデル化されていない借り(FFIへの値へのポインタの渡しなど)を表すのに(時には)役立ちます。

+0

この大雑把な経験則のために受け入れられました。ありがとう。 – jferard

1

私はmain()f4()の体を入れて、それがドロップされたときに調べるためにBar2ためDropを実装(すなわち、スコープの外に出る):

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 

Something's fishy;のは、ヘルパーのスコープで、具体的にそれを調べてみましょう:

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の可変ボローを保持していることを意味します。

関連する問題