2015-11-28 9 views
11

私は、によってRustと遊んでいます。これは、OCamlでの私の機能スタイルの実装に基づいています。私は、Rustが機能スタイルのコードでどのように運賃を払っているかを見たいと思っていました。Find Closureの引数に2つのアンパサンドが必要なのはなぜですか?

最終結果:これは動作し、非常に高速です。これはOCamlよりはるかに高速です。それは、命令型C/C++の速度にほとんど触れています。これは本当にクールです。

私には問題がありますが、このコードの最後の行に2つのアンパサンドが必要なのはなぜですか?

let moves_and_scores: Vec<_> = moves_and_boards 
    .iter() 
    .map(|&(column,board)| (column, score_board(&board))) 
    .collect(); 
let target_score = if maximize_or_minimize { 
    ORANGE_WINS 
} else { 
    YELLOW_WINS 
}; 
if let Some(killer_move) = moves_and_scores.iter() 
    .find(|& &(_,score)| score==target_score) { 
     ... 

私はそれらを追加したのは、コンパイラエラーが私にそれを「誘導」しているからです。しかし、私は理由を理解しようとしています...私は、コンパイラがどのようなタイプのものを私に伝えるために、「尋ねる」ためにスタックオーバーフローの他の場所で言及したトリックを使用:

:このエラーの原因

let moves_and_scores: Vec<_> = moves_and_boards 
    .iter() 
    .map(|&(column,board)| (column, score_board(&board))) 
    .collect(); 
let() = moves_and_scores; 

...

src/main.rs:108:9: 108:11 error: mismatched types: 
expected `collections::vec::Vec<(u32, i32)>`, 
    found `()` 
(expected struct `collections::vec::Vec`, 
    found()) [E0308] 
src/main.rs:108  let() = moves_and_scores; 

...期待どおり、moves_and_scoresはタプルのベクトルです:Vec<(u32, i32)>です。しかし、その後、すぐに次の行に、iter()find()クロージャパラメータに恐ろしい重アンパサンドを使用するように私を強制:

if let Some(killer_move) = moves_and_scores.iter() 
    .find(|& &(_,score)| score==target_score) { 

はなぜfind閉鎖は2つのアンパサンドが必要なのですか?私はそれがなぜ必要なかもしれないのかを知ることができました(時間と空間を節約するために参照によってタプルを渡します)が、なぜ2つですか?それはiterのためですか?つまり、は参照を作成し、次にfindは各入力の参照を期待しているので、参照の参照は必要ですか?

これがそうであれば、間違いなく、Rustのかなり醜いデザインの欠陥ですか?

実際、findmapと残りのすべての機能プリミティブは、コレクション自体の一部になると思います。私がiter()に何らかの機能スタイルの作業を強制することは、すべての可能な機能チェーンでこのような「二重アンパサンド」を強制するとさらに負担になります。

私は何か明白であることを望んでいます - ご迷惑をおかけしておりません。

+0

ポートを管理していただきありがとうございます。機能スタイルコードにOCamlを打つことは、あなたが正しいことをしたことを意味します! @MatthieuM。 –

+0

ありがとう!私は機能的なチェーン(すなわち '.iter()。map(...)iter()。filter()... .iter()。find(...)')を処理するためのよりきれいな方法があることを期待していました。すべてのステップで追加のレベルの参照を導入することなく - しかし、私はそれを避けることはできないようだ。 – ttsiodras

答えて

13

ここでこの

moves_and_scores.iter() 

はあなたに借りベクトルの要素の反復子を提供します。あなたはこれがどのような種類のAPIドキュメントに従っている場合、あなたはそれが借りスライスのためだけのイテレータだということに気づくでしょう、これはTはあなたのケースで(u32, i32)あるItem=&TIteratorを実装しています。

次に、&Itemをパラメータとする述語を取るfindを使用します。シスItemはあなたの場合は既に参照です、述語は&&(u32, i32)を取らなければなりません。

pub trait Iterator { 
    ... 
    fn find<P>(&mut self, predicate: P) -> Option<Self::Item> 
    where P: FnMut(&Self::Item) -> bool {...} 
    ...   ^

唯一の項目を検査し、ブール値を返すことになっていますので、それはおそらくthisのように定義されました。これは、値渡しのアイテムを必要としません。 TCloneある場合

あなたは(u32, i32)の反復子をしたい場合は、

moves_and_scores.iter().cloned() 

cloned()を書くことができはItemタイプTItemタイプ&T 1へと1からイテレータを変換します。これを行うもう1つの方法は、iter()の代わりにinto_iter()を使用することです。

moves_and_scores.into_iter() 

2つの違いは、最初のオプションクローン借り要素2つのベクトルを消費し、そこから要素を移動しながら、ということです。

この

|&&(_, score)| score == target_score 

のようにラムダを書き込むことにより、あなたは「二重の参照を」destructureとi32のローカルコピーを作成します。 i32Copyという単純なタイプなので、これは許可されています。代わりに、必要に応じてドット演算子が自動的に何度でも参照解除ので、あなたはまた、

|move_and_score| move_and_score.1 == target_score 

を書くことができ、あなたの述語のパラメータを構造化代入の

+0

ご意見ありがとうございます! 'iter().cloned()'はパフォーマンスに影響しますか?つまり、実際には「クローン」を割り当てるのですか?また、動くセマンティクスがソースベクトルを変更するので、 'into_iter()'は明らかにパフォーマンスに影響を与えます。私はあなたの3番目の提案がベストだと思っています - 「必要に応じて何度も自動逆参照を使用する」 - それを読まなければなりません。 – ttsiodras

+0

ところで、Rustがコレクションの一部として関数演算子(map、filterなど)を持たないことを選択し、 '.iter()'を最初に必要とする理由( "参照インダイレクション"各ステップで)....? – ttsiodras

+1

@ttsiodrasパフォーマンスに関して:それは本当に重要ではありません。 '(u32、i32) '型のタプルのクローン作成は非常に安価です。 "クローン"という言葉があなたを恐れさせないでください。 :-) simpeの "普通の古いデータ型"の複製とコピーは基本的に同じです。しかし、いくつかのタイプはより複雑なのでコピーできません。そのような場合にのみ、私はクローニングについて心配します。 – sellibitze

関連する問題