2016-01-23 8 views
26

私は、イテレータを受け取り、その上でいくつかの演算の結果を返す関数を記述したいと思います。具体的には、私はHashMapの値を反復しようとしています:イテレータをとる錆関数を書くには?

use std::collections::HashMap; 

fn find_min<'a>(vals: Iterator<Item=&'a u32>) -> Option<&'a u32> { 
    vals.min() 
} 

fn main() { 
    let mut map = HashMap::new(); 
    map.insert("zero", 0u32); 
    map.insert("one", 1u32); 
    println!("Min value {:?}", find_min(map.values())); 
} 

しかし、悲しいかな:

error: the `min` method cannot be invoked on a trait object 
--> src/main.rs:4:10 
    | 
4 |  vals.min() 
    |   ^^^ 

error[E0277]: the trait bound `std::iter::Iterator<Item=&'a u32> + 'static: std::marker::Sized` is not satisfied 
--> src/main.rs:3:17 
    | 
3 | fn find_min<'a>(vals: Iterator<Item = &'a u32>) -> Option<&'a u32> { 
    |     ^^^^ `std::iter::Iterator<Item=&'a u32> + 'static` does not have a constant size known at compile-time 
    | 
    = help: the trait `std::marker::Sized` is not implemented for `std::iter::Iterator<Item=&'a u32> + 'static` 
    = note: all local variables must have a statically known size 

error[E0308]: mismatched types 
    --> src/main.rs:11:41 
    | 
11 |  println!("Min value {:?}", find_min(map.values())); 
    |           ^^^^^^^^^^^^ expected trait std::iter::Iterator, found struct `std::collections::hash_map::Values` 
    | 
    = note: expected type `std::iter::Iterator<Item=&u32> + 'static` 
       found type `std::collections::hash_map::Values<'_, &str, u32>` 

私は参照渡ししようとした場合、私は同じエラーを取得します。 Boxを使用すると、生涯エラーが発生します。

答えて

21

あなたはここにジェネリックを使用したい:

fn find_min<'a, I>(vals: I) -> Option<&'a u32> 
where 
    I: Iterator<Item = &'a u32>, 
{ 
    vals.min() 
} 

形質は2つの方法で使用することができます。型パラメータの境界としておよび形質オブジェクトとして。 錆プログラミング言語には、traitsに関する章と、これら2つの使用例を説明するtrait objectsの章があります。

また、あなたは多くの場合、このようIntoIteratorがよりよいあなたの関数を呼び出すコードを作ることができます実装何かをしたい:

fn find_min<'a, I>(vals: I) -> Option<&'a u32> 
where 
    I: IntoIterator<Item = &'a u32>, 
{ 
    vals.into_iter().min() 
} 
+0

私はとても近くでした。解説すると、ジェネリックとの違いは静的ディスパッチです。つまり、私はそれを呼び出す具体的なタイプごとにこの関数のバージョンを作成していますか? –

+2

正しい。このように、コンパイラはイテレータの型を知っているので、そのサイズを知っているので、どのくらいのメモリを確保する必要があるのか​​が分かります。また、 'Iterator 'のような型は単独では使用できません。ポインタの後ろでしか使用できません。 –

+6

任意のイテレータを関数に渡したい場合は、 'Iterator'を実装するために' I'を必要としますが、より汎用的な方法は '' IntoIterator''(http:// doc.rust-lang.org/std/iter/trait.IntoIterator.html)。イテレータも渡すことができますが、変換メソッドを明示的に呼び出す必要はなく、イテレータに変換できるものを渡すこともできます。私はイテレータとiterablesを消費する*慣用的なアプローチだと言っています。 –

4

この動作は、たとえば、ではなくPythonの背景を持つものとは少しわかりにくいですC++のバックグラウンドですので、少しはっきりさせてください。

Rustでは、値は概念的にバインドされた名前の内部に格納されます。したがって、あなたは

let mut x = Foo { t: 10 }; 
let mut y = x; 
x.t = 999; 

y.tを書いた場合、まだ10になります。

ですから、(関数の引数リストまたは同じ)

let x: Iterator<Item=&'a u32>; 

を書くとき、錆タイプIterator<Item=&'a u32>任意の値に対して十分なスペースを割り当てる必要があります。これが可能であっても、それは効率的ではありません。

だから何錆が代わりに行うことは例えば、ヒープ上の値を入れて

  • にあなたのオプションを提供しています。 Boxで、Pythonスタイルのセマンティクスを提供します。その後、&mut Iterator<Item=&'a u32>と総称します。

  • バインドを満たす可能性のある各タイプの各関数呼び出しを特殊化します。特性参照は可能な特殊化であり、コンパイラーに特殊化の機会を与えますが、動的ディスパッチ(実行時パラメーターによってタイプが異なる場合があります)を行うことはできません。

関連する問題