代码之家  ›  专栏  ›  技术社区  ›  Ian

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

  •  4
  • Ian  · 技术社区  · 6 年前

    我有以下函数,它应该查找并返回 String 给予 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;
    }
    

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

    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`
    

    我对Rust还不熟悉,对此非常困惑,因为我认为我是在显式地传递一个迭代器。打电话 strings.iter() 告诉我它没有实现,并调用 strings.into_iter() 把我送进了一个易变的兔子洞,我当然不想改变已经通过的论点。

    如何在字符串上迭代?

    3 回复  |  直到 6 年前
        1
  •  9
  •   Tim Diekmann suresh madaparthi    6 年前

    你的代码失败是因为 Iterator &Iterator . 如果你通过的话你可以解决这个问题 迭代器 为了你的职责,但是自从 迭代器 是一个特征,大小无法确定(你不知道,什么 迭代器 你正在通过)。解决方案是传递实现 迭代器 :

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

    playground


    对于更有经验的铁锈用户:

    最通用的方法可能是:

    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

        2
  •  4
  •   Shepmaster Tim Diekmann    6 年前

    其他的答案告诉你 怎样 接受迭代器,但不必回答实际问题:

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

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

    我当然不想改变已经通过的论点

    迭代器通过改变其目标来工作——这就是迭代器如何更改以返回每个调用的新值!

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

    接受一个不变的 特征对象 ,迭代器不可能更新自身,因此不可能实际迭代。

    要编译代码,您所能做的最小的事情就是接受可变引用:

    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. 使用迭代器组合 map max
    3. 使用 Option::unwrap_or 提供默认值。
    4. 使用 IntoIterator 接受任何可以生成迭代器的内容。
        3
  •  1
  •   Matt Harrison    6 年前

    如果您不特别需要遍历任何给定迭代器的通用性,那么编写该函数的一个更简单的方法是 max_width 函数take a &[&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