为什么不能将`&Iterator <item>`用作迭代器?

时间:2018-08-09 03:42:45

标签: rust

我有以下函数,该函数应该查找并返回给定String的{​​{1}}的最长长度:

Iterator

但是,编译器给我以下错误:

fn max_width(strings: &Iterator<Item = &String>) -> usize {
    let mut max_width = 0;
    for string in strings {
        if string.len() > max_width {
            max_width = string.len();
        }
    }
    return max_width;
}

我是Rust的新手,对此感到非常困惑,因为我认为我明确地传递了一个迭代器。调用error[E0277]: the trait bound `&std::iter::Iterator<Item=&std::string::String>: std::iter::Iterator` is not satisfied --> src/main.rs:3:19 | 3 | for string in strings { | ^^^^^^^ `&std::iter::Iterator<Item=&std::string::String>` is not an iterator; maybe try calling `.iter()` or a similar method | = help: the trait `std::iter::Iterator` is not implemented for `&std::iter::Iterator<Item=&std::string::String>` = note: required by `std::iter::IntoIterator::into_iter` 告诉我它没有实现,而调用strings.iter()则使我陷入了一个易变性的陷阱,而且我当然不想突变传递的参数。

如何遍历我的琴弦?

3 个答案:

答案 0 :(得分:8)

您的代码失败,因为Iterator&Iterator不同。如果将Iterator传递给函数,则可以解决此问题,但是由于Iterator是一个特征,因此无法确定大小(您不知道传递的是Iterator)。解决方案是传递实现Iterator的所有内容:

fn max_width<'a>(strings: impl Iterator<Item = &'a String>) -> usize

playground


对于经验丰富的Rust用户:

最通用的方式可能是这样:

fn max_width<T: AsRef<str>>(strings: impl IntoIterator<Item = T>) -> usize {
    let mut max_width = 0;
    for string in strings {
        let string = string.as_ref();
        if string.len() > max_width {
            max_width = string.len();
        }
    }
    max_width
}

playground

但是,您也可以使用

fn max_width<T: AsRef<str>>(strings: impl IntoIterator<Item = T>) -> usize {
    strings
        .into_iter()
        .map(|s| s.as_ref().len())
        .max()
        .unwrap_or(0)
}

playground

答案 1 :(得分:3)

其他答案向您展示如何接受迭代器,但掩盖了回答您实际问题的可能性:

  

为什么不能使用&Iterator<Item = &String>作为迭代器?

有趣的是,您自己阻止了它:

  

我当然不想改变传递的参数

迭代器通过改变目标来工作,这就是迭代器可以更改以为每次调用返回新值的方式!

pub trait Iterator {
    type Item;
    fn next(&mut self) -> Option<Self::Item>;
    //       ^^^
}

通过引入一个不变的 trait对象,您的迭代器无法更新自身,因此无法进行实际迭代。

您可以使代码编译的绝对最小的事情是接受可变的引用:

fn max_width(strings: &mut dyn Iterator<Item = &String>) -> usize

但是,我可能将函数编写为:

fn max_width<I>(strings: I) -> usize
where
    I: IntoIterator,
    I::Item: AsRef<str>,
{
    strings
        .into_iter()
        .map(|s| s.as_ref().len())
        .max()
        .unwrap_or(0)
}
  1. 请勿使用明确的return
  2. 使用mapmax之类的迭代器组合器
  3. 使用Option::unwrap_or提供默认值。
  4. 使用IntoIterator接受可以构成迭代器的所有内容。

答案 2 :(得分:0)

如果您不特别需要在任何给定的迭代器上进行迭代的通用性,则编写函数的更简单方法是让您的max_width函数采用&[&str](字符串切片的一部分)代替。您可以在for循环中使用切片,因为Rust知道如何将其变成迭代器(它实现了IntoIterator特性):

fn max_width(strings: &[&str]) -> usize {
    let mut max_width = 0;
    for string in strings {
        if string.len() > max_width {
            max_width = string.len();
        }
    }
    return max_width;
}

fn main() {
    let strings = vec![
        "following",
        "function",
        "supposed",
        "return",
        "longest",
        "string",
        "length"
    ];

    let max_width = max_width(&strings);

    println!("Longest string had size {}", max_width);
}

// OUTPUT: Longest string had size 9

Playground here