如何编写一个带迭代器的Rust函数?

时间:2016-01-23 22:48:42

标签: function iterator 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,我会遇到终生错误。

3 个答案:

答案 0 :(得分:38)

你想在这里使用泛型:

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

可以通过两种方式使用特征:作为类型参数的边界和特征对象。本书 The Rust Programming Language 有一章traits和一章trait objects解释这两个用例。

此外,您经常需要采用实现IntoIterator的内容,因为这可以使代码调用您的函数更好:

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

答案 1 :(得分:12)

这种行为与那些具有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>;

(或在函数参数列表中相同),Rust需要为类型Iterator<Item=&'a u32>的任何值分配足够的空间。即使这是可能的,也不会有效。

所以Rust所做的就是为你提供选择

  • 将值放在堆上,例如。使用Box,它提供了Python风格的语义。然后,您可以使用&mut Iterator<Item=&'a u32>

  • 专门为每个可能的类型调用每个函数以满足绑定。这更灵活,因为特征引用是一种可能的特化,并为编译器提供了更多的专业化机会,但意味着您不能拥有动态分派(其中类型可能因运行时参数而异)

答案 2 :(得分:0)

最简单的方法是使用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()));
}

playground