编写一个函数来获取引用和值类型

时间:2017-12-03 14:27:41

标签: rust

我想要一个带迭代的函数并返回其最小和最大的元素。这是学习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);
}

2 个答案:

答案 0 :(得分:3)

  

我希望有一个可迭代的函数,并返回其最小和最大的元素。

使用Itertools::minmax

  

同时处理引用类型和值类型。

您不需要 - 对数字的引用也可以进行比较:

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()调用会生成一个“切片”迭代器(IteratorItem = &T),但.map(|x| 2 * x)是一个迭代器适配器, call的生成一个新的“值”迭代器(IteratorItem = 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一章。