[英]How to write a Rust function that takes an iterator?
I'd like to write a function that accepts an iterator and returns the results of some operations on it.我想编写一个接受迭代器并返回对其进行某些操作的结果的函数。 Specifically, I'm trying to iterate over the values of a
HashMap
:具体来说,我试图遍历
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()));
}
But alas:可惜:
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>`
I get the same error if I try to pass by reference;如果我尝试通过引用传递,我会得到同样的错误; if I use a
Box
, I get lifetime errors.如果我使用
Box
,我会遇到生命周期错误。
You want to use generics here:你想在这里使用泛型:
fn find_min<'a, I>(vals: I) -> Option<&'a u32>
where
I: Iterator<Item = &'a u32>,
{
vals.min()
}
Traits can be used in two ways: as bounds on type parameters and as trait objects.特征可以以两种方式使用:作为类型参数的边界和作为特征对象。 The book The Rust Programming Language has a chapter on traits and a chapter on trait objects that explain these two use cases.
《 The Rust Programming Language 》一书有一章是关于traits的,还有一章是关于trait objects的,它们解释了这两个用例。
Additionally, you often want to take something that implements IntoIterator
as this can make the code calling your function nicer:此外,您通常希望采用实现
IntoIterator
的东西,因为这可以使调用您的函数的代码更好:
fn find_min<'a, I>(vals: I) -> Option<&'a u32>
where
I: IntoIterator<Item = &'a u32>,
{
vals.into_iter().min()
}
Since Rust 1.26 impl Trait are available.自 Rust 1.26 impl Trait可用。 Here is a more compact version using
impl Trait
.这是使用
impl Trait
的更紧凑的版本。
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()));
}
This behaviour is a little unintuitive from those with a Python background rather than, say, a C++ background, so let me clarify a little.对于那些具有 Python 背景而不是 C++ 背景的人来说,这种行为有点不直观,所以让我澄清一下。
In Rust, values are conceptually stored inside the name that binds them.在 Rust 中,值在概念上存储在绑定它们的名称中。 Thus, if you write
因此,如果你写
let mut x = Foo { t: 10 };
let mut y = x;
x.t = 999;
yt
will still be 10
. yt
仍然是10
。
So when you write所以当你写
let x: Iterator<Item=&'a u32>;
(or the same in the function parameter list), Rust needs to allocate enough space for any value of type Iterator<Item=&'a u32>
. (或在函数参数列表中相同),Rust 需要为
Iterator<Item=&'a u32>
类型的任何值分配足够的空间。 Even if this was possible, it wouldn't be efficient.即使这是可能的,它也不会有效。
So what Rust does instead is offer you the option to所以 Rust 所做的是为您提供选择
Put the value on the heap, eg.将值放在堆上,例如。 with
Box
, which gives Python-style semantics. with
Box
,它提供了 Python 风格的语义。 Then you can take generically with &mut Iterator<Item=&'a u32>
.然后你可以一般地使用
&mut Iterator<Item=&'a u32>
。
Specialize each function invocation for each possible type to satisfy the bound.为每种可能的类型专门化每个函数调用以满足边界。 This is more flexible, since a trait reference is a possible specialization, and gives the compiler more opportunities for specialization, but means you can't have dynamic dispatch (where the type can vary dependent on runtime parameters).
这更灵活,因为特征引用是一种可能的特化,并为编译器提供了更多的特化机会,但这意味着您不能进行动态分派(类型可以根据运行时参数而变化)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.