2016-08-12 2 views
4

錆書のError Handling chapterには、OptionResultという組み合わせを使用する方法の例が含まれています。ファイルが読み込まれ、一連のコンビネータを適用すると、コンテンツはi32として解析され、Result<i32, String>に返されます。 コードを見ると混乱します。ここで、and_thenへの1つのクロージャでは、ローカルのString値が作成され、その後、別のコンビネータに戻り値として渡されます。ここでなぜ借りたのではなく、クロージャーに値を移動したのですか?

はコードの例です:私が参照しています

use std::fs::File; 
use std::io::Read; 
use std::path::Path; 

fn file_double<P: AsRef<Path>>(file_path: P) -> Result<i32, String> { 
    File::open(file_path) 
     .map_err(|err| err.to_string()) 
     .and_then(|mut file| { 
       let mut contents = String::new(); // local value 
       file.read_to_string(&mut contents) 
        .map_err(|err| err.to_string()) 
        .map(|_| contents) // moved without 'move' 
     }) 
     .and_then(|contents| { 
       contents.trim().parse::<i32>() 
         .map_err(|err| err.to_string()) 
     }) 
     .map(|n| 2 * n) 
} 

fn main() { 
    match file_double("foobar") { 
     Ok(n) => println!("{}", n), 
     Err(err) => println!("Error: {}", err), 
    } 
} 

値はcontentsです。これは、std::io::Result<usize>戻り値Read::read_to_stringに適用されたmapコンビネータで作成され、後で参照されます。 質問:私はではなくと閉鎖をマークするとデフォルトで参照された値が借用され、貸し出しチェッカーが文句を言うことになります。contentsは十分に長くは生きていません。しかし、このコードはうまくコンパイルされます。つまり、Stringcontentsはクロージャの中に移動され、クロージャの下位に移動されます。これが明示的なmoveなしで行われるのはなぜですか?

答えて

3

私はが移動して閉鎖をマーキングしませする

ない、非常に、デフォルトですべての参照値を借りるだろうと思いました。コンパイラは、クロージャ本体内のコードの検査を少し行い、クローズドオーバー変数の使用方法を追跡します。

コンパイラが方法を変数に呼び出されたことを確認すると、それは受信機は(self&self&mut self)でどのようなタイプかを確認します。変数がパラメータとして使用されるとき、コンパイラは値、参照、または可変参照であるかどうかも追跡します。最も制限の厳しい要件は、デフォルトで使用されるものになります。

場合によっては、この分析が十分ではない - 変数が参照としてのみ使用されていても、クロージャが変数を所有することを意図しています。これは、通常、クロージャを返すか、別のスレッドに渡すときに発生します。

この場合、変数は値によって使用されることを意味するクロージャから返されます。したがって、変数は自動的にクロージャーに移動されます。それはで参照される変数のすべてを動かすよう


は時折moveキーワードは、ハンマーの大きすぎる。時には、あなただけの他人をに移動ではなく、することが一つの変数を強制することもできます。その場合は、私が知っている最良の解決策は明示的に参照すると、参照移動することです:マッピングするクロージャを引数としてUSIZEを渡されることを

fn main() { 
    let a = 1; 
    let b = 2; 

    { 
     let b = &b; 
     needs_to_own_a(move || a_function(a, b)); 
    } 
} 
+0

をし、それが文字列を返します。したがって、String値はクロージャ環境から移動されるため、最初に移動する必要があります。これは基本的にここのコンパイラの理由ですか? –

+0

@ JonasTepeは右のように聞こえます。クロージャー内のある関数/メソッド呼び出しのために値によって使用されていた場合にも移動されていました。 – Shepmaster

関連する問題