web-dev-qa-db-ja.com

イテレータをとるRust関数を書くには?

イテレータを受け入れ、それに対するいくつかの操作の結果を返す関数を作成したいと思います。具体的には、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を使用すると、ライフタイムエラーが発生します。

47
Doctor J

ここでジェネリックを使用します:

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

特性は、型パラメーターの境界として、および特性オブジェクトとしての2つの方法で使用できます。本Rust Programming Languageには traits に関する章と-に関する章があります 特性オブジェクト これら2つの使用例を説明します。

さらに、IntoIteratorを実装するものを使用することがよくあります。これにより、関数を呼び出すコードがより適切になります。

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

この振る舞いは、たとえばC++の背景ではなく、Pythonバックグラウンドを持つものとは少し直感的ではないので、少し明確にしましょう。

Rustでは、値は概念的にそれらをバインドする名前内に保存されます。したがって、あなたが書く場合

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

y.t10のままです。

だから書くとき

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

(または関数パラメーターリストで同じ)、Rust anyタイプIterator<Item=&'a u32>の値に十分なスペースを割り当てる必要があります。これが可能であったとしても、効率的ではありません。

だからRustが代わりに行うことはあなたにオプションを提供することです

  • ヒープに値を置きます。 Boxを使用して、Pythonスタイルのセマンティクスを提供します。次に、&mut Iterator<Item=&'a u32>を一般的に使用できます。

  • 可能性のある型ごとに各関数呼び出しを特殊化し、境界を満たします。特性参照は特殊化の可能性があり、コンパイラーに特殊化の機会を与えるため、これはより柔軟性がありますが、dynamicディスパッチ(実行時パラメーターに応じて型が変化する可能性がある)を持たないことを意味します。

12
Veedrac

最も簡単なのは impl Traitimpl Iterator

use std::collections::HashMap;

fn find_min<'a>(vals: impl 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()));
}

遊び場

0
Ben