2016-01-23 64 views
26

我想编写一个接受迭代器并返回一些操作结果的函数。具体来说,我想遍历一个HashMap值:如何编写一个需要迭代器的Rust函数?

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() 
} 

性状可以以两种方式使用:作为类型参数的边界和特质的对象。这本书生锈的程序设计语言有一章traitstrait objects章节解释这两个用例。

此外,你经常要带的东西,实现了IntoIterator,因为这可以使代码调用你的函数更好:

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

我太亲近了。为了说明,与泛型的区别是静态分派,即Rust为我调用的每个具体类型创建了此函数的一个版本? –

+2

正确。这样,编译器知道迭代器的类型,因此它知道它的大小,所以它知道需要预留多少内存。另外,像Iterator '这样的类型不能单独使用;它只能在指针后面使用。 –

+6

请注意,虽然要求'I'实现'Iterator'是绝对正确的,如果你想传递任意迭代器到函数中,更通用的方法是要求'I'实现['IntoIterator'](http:// doc.rust-lang.org/std/iter/trait.IntoIterator.html)。它允许你也传递迭代器,但你也可以将任何可以被转换的东西传递给一个迭代器,而不需要明确地调用转换方法。我会说这是消费迭代器和迭代器的常用方法。 –

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>

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

相关问题