2016-08-02 4 views
9

借り手のスコープが終了した後でも変更可能な借用変数が借用されている理由を理解できません。使用を形質に関連しているように見えますが、私はなぜ表示されない:借り手のスコープが終了したときに変数を借りることができません。

fn main() { 
    let mut a = 10; 
    test::<FooS>(&mut a); 
    println!("out {:?}", a) 
} 

trait Foo<'a> { 
    fn new(data: &'a mut u32) -> Self; 
    fn apply(&mut self); 
} 

struct FooS<'a> { 
    data: &'a mut u32, 
} 

impl<'a> Foo<'a> for FooS<'a> { 
    fn new(data: &'a mut u32) -> Self { 
     FooS { data: data } 
    } 

    fn apply(&mut self) { 
     *self.data += 10; 
    } 
} 

fn test<'a, F>(data: &'a mut u32) 
    where F: Foo<'a> 
{ 
    { 
     // let mut foo = FooS {data: data}; // This works fine 
     let mut foo: F = Foo::new(data); 
     foo.apply(); 
    } // foo scope ends here 
    println!("{:?}", data); // error 
} // but borrowed till here 

try online

error: cannot borrow `data` as immutable because `*data` is also borrowed as mutable [--explain E0502] 
    --> <anon>:34:22 
31 |>   let mut foo: F = Foo::new(data); 
    |>         ---- mutable borrow occurs here 
... 
34 |>  println!("{:?}", data); // error 
    |>      ^^^^ immutable borrow occurs here 
35 |> } // but borrowed till here 
    |> - mutable borrow ends here 
+1

https://play.rust-lang.org/?gist=dabe17d66a14e72c2ca67e064ca26601&version=nightly&backtrace=0作品。 so ... – Jacob

+0

それでも、これはバグのようです。 – JDemler

答えて

8

test機能はタイプFFoo<'a>を実装する必要があります。 'aには、関数に渡される存続時間パラメータがあります。寿命パラメータは、関数呼び出しよりも長く存続する寿命を常に表します。呼び出し元が寿命の短い参照を提供できる方法がないためです。どのようにから別の関数のローカル変数への参照を渡すことができますか? - と、(関数にローカルな)借り入れ検査の目的のために、借用は関数呼び出し全体をカバーしているとみなします。

したがって、Foo::newへの呼び出しからFのインスタンスを作成すると、生存期間が'aであるものを借りるオブジェクトを作成します。このオブジェクトは、関数呼び出し全体をカバーしています。

それはあなたがtest::<FooS>呼び出すときに、コンパイラは実際にFooS<'a>の有効期間パラメータを埋めていることを理解することが重要ですので、あなたは&mut aがあるので'aが(関数呼び出しを含むステートメントをカバー領域にあるtest::<FooS<'a>>を呼び出すに終わります一時的な表現)。したがって、testに呼び出されたFooSは、testを呼び出してステートメントの最後まで何かを借りると考えています。

のは、非ジェネリックバージョンでこれを対比してみましょう:このバージョンで

let mut foo = FooS {data: data}; 

、コンパイラはむしろmainよりも、testFooS<'a>のための具体的な寿命を選ぶので、端から延びるブロック接尾辞を選択しますブロックの末尾にletステートメントの次の借用は、dataの借りが重複せず、競合がないことを意味します。あなたが本当に欲しい

F'aよりも短くなって、そして最も重要なのは、その寿命が関数内の領域である必要があり、'aのような囲み1ではありませんいくつかの生涯'xためFoo<'x>を実装することです。

この問題に対するRustの現在の解決策は、より高いランクの形質境界です。それはタイプFが可能なすべての'xためFoo<'x>を実装する必要があり意味、すなわち

fn test<'a, F>(data: &'a mut u32) 
    where F: for<'x> Foo<'x> 
{ 
    { 
     let mut foo: F = Foo::new(data); 
     foo.apply(); 
    } 
    println!("{:?}", data); 
} 

:それはこのようになります。

testのこのバージョンは、独自にコンパイルしますが、すべての可能な寿命'aのために、唯一のFoo<'a>を実装して特殊タイプFooS<'a>があるので、私たちは実際に、この制約を満たし種類を供給することができません。FooSは一切寿命パラメータを持っていたとFooSためFooのIMPLはこのように見えた場合:

impl<'a> Foo<'a> for FooS { 

あらゆる可能な寿命'aためFoo<'a>を実装し、単一のタイプFooSがあるので、それは、罰金になります。

借用したポインタが含まれているので、FooSのライフタイムパラメータを削除することはできません。この問題の適切な解決策は、Rustがまだ持っていない機能です。つまり、型コンストラクタ(完全に構築された型ではなく)を汎用パラメータとして関数に渡すことです。この機能を使用すると、testをと呼ぶことができます。タイプコンストラクタは、コールサイトでの具体的な有効期間を指定せずに、具体的な型を生成するための生涯パラメータを必要とし、呼び出し元は独自のライフタイムを提供できます。

+0

詳細な回答ありがとうございます。まだ私にとっては不明な瞬間です。あなたが言ったように_生涯パラメータは常に関数呼び出しより長く生きる生存期間を表し、(関数にローカルな)借り入れチェックの目的では、関数呼び出し全体が借りているとみなします。なぜコンパイラコンパイラはこれを考慮する?私たちは生存期間が長く、機能範囲が長くなっています。しかし、それが借用にどのように関連して、関数呼び出し全体をカバーしていますか?実装の詳細か仕様の一部ですか? –

+0

まだRustの "仕様"は適切ではないので、実装は仕様に最も近いものです。他の質問については、私の編集を参照してください。 –

関連する問題