我想要一个带迭代的函数并返回其最小和最大的元素。这是学习Rust的练习的一部分,但我很难同时处理引用类型和值类型。
这就是我所拥有的:
fn min_max<'a, I, T>(mut iter: I) -> Option<(&'a T, &'a T)>
where
I: Iterator<Item = &'a T>,
T: PartialOrd,
{
let mut min = match iter.next() {
Some(x) => x,
// The collection is empty
None => return None,
};
let mut max = min;
for el in iter {
if el < min {
min = el;
}
if el >= max {
max = el;
}
}
Some((min, max))
}
然后,我给它一个整数的迭代器。
let nums: [u32; 6] = [4, 3, 9, 10, 4, 3];
if let Some((min, max)) = min_max(nums.iter()) {
println!("{} {}", min, max);
}
这样可行,并打印3 10
。但是我想在计算最小值和最大值之前对数字进行一些操作,例如map
和/或filter
。
let doubled = nums.iter().map(|x| 2 * x);
if let Some((min, max)) = min_max(doubled) {
println!("{} {}", min, max);
}
这会产生编译错误:
error[E0271]: type mismatch resolving `<[closure@src/main.rs:31:35: 31:44] as std::ops::FnOnce<(&u32,)>>::Output == &_`
--> src/main.rs:32:31
|
32 | if let Some((min, max)) = min_max(doubled) {
| ^^^^^^^ expected u32, found reference
|
= note: expected type `u32`
found type `&_`
= note: required because of the requirements on the impl of `std::iter::Iterator` for `std::iter::Map<std::slice::Iter<'_, u32>, [closure@src/main.rs:31:35: 31:44]>`
= note: required by `min_max`
这使我感到困惑,因为如果nums.iter()
作为论据,为什么不应该nums.iter().map(...)
?
我原则上理解错误消息:我的数组属于u32
,而不是&u32
,而我的函数要求Iterator::Item
属于&'a T
类型。但后来我不明白为什么它只在第二个样本(使用.iter().map()
)而不是第一个样本(仅.iter()
)上出错。
我用这个例子做了一个playground和一个注释掉的例子,我从字符串构造一个可迭代的整数。这与上面的第二个示例完全相同(并且更接近我的实际用例)。
let s = "4 3 9 10 4 3";
let parsed = s.split(" ").map(|x| x.parse::<u32>().unwrap());
if let Some((min, max)) = min_max(parsed) {
println!("{} {}", min, max);
}
答案 0 :(得分:3)
我希望有一个可迭代的函数,并返回其最小和最大的元素。
同时处理引用类型和值类型。
您不需要 - 对数字的引用也可以进行比较:
fn foo(a: &i32, b: &i32) -> bool {
a < b
}
在您的情况下,请记住该值和对该值的引用是不同类型。这意味着你可以接受任何类型的迭代器,只要产生的值是可比较的,并且这包括引用和值,如下所示:
fn min_max<I>(mut iter: I) -> Option<(I::Item, I::Item)>
where
I: Iterator,
I::Item: Clone + PartialOrd,
{
let mut min = match iter.next() {
Some(x) => x,
// The collection is empty
None => return None,
};
let mut max = min.clone();
for el in iter {
if el < min {
min = el;
} else if el >= max {
max = el;
}
}
Some((min, max))
}
我选择添加Clone
绑定虽然更加真实,但我可以使用Copy
绑定。 Itertools返回一个枚举,以避免对能够复制值的任何限制。
这适用于您的所有三个示例:
fn main() {
let nums: [u32; 6] = [4, 3, 9, 10, 4, 3];
if let Some((min, max)) = min_max(nums.iter()) {
println!("{} {}", min, max);
}
let doubled = nums.iter().map(|x| 2 * x);
if let Some((min, max)) = min_max(doubled) {
println!("{} {}", min, max);
}
let s = "4 3 9 10 4 3";
let parsed = s.split(" ").map(|x| x.parse::<u32>().unwrap());
if let Some((min, max)) = min_max(parsed) {
println!("{} {}", min, max);
}
}
3 10
6 20
3 10
我的数组是
u32
,而不是&u32
,而我的函数要求Iterator::Item
为&'a T
类型。但是,我不知道为什么它只在第二个样本(使用.iter().map()
)而不是第一个样本(仅.iter()
)上出错。
因为iterating over an array returns references。通过使用map
,您将迭代器项的类型从&i32
更改为i32
。你也可以chosen to adapt the first call to return values。
答案 1 :(得分:0)
您遇到类型不匹配问题,因为.iter()
调用会生成一个“切片”迭代器(Iterator
和Item = &T
),但.map(|x| 2 * x)
是一个迭代器适配器, call的生成一个新的“值”迭代器(Iterator
与Item = T
)。在我们将它们“切片”之前,必须将这些值存储在内存中,因为我们只能获得对已经存储在内存中某个位置的值的引用。因此,我们需要先收集map
函数的结果,然后才能获得一个迭代器,并引用它返回的值:
let doubled: Vec<_> = nums.iter().map(|x| 2 * x).collect();
if let Some((min, max)) = min_max(doubled.iter()) {
println!("{} {}", min, max);
}
有关详细信息,请参阅The Rust Programming Language一书的13.2 Iterators一章。