【问题标题】:Why can't I use `&Iterator<Item = &String>` as an iterator?为什么我不能使用 `&Iterator<Item = &String>` 作为迭代器?
【发布时间】:2018-08-09 20:37:08
【问题描述】:

在给定Iterator 的情况下,我有以下函数应该查找并返回String 的最长长度:

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() 让我陷入了一个可变的兔子洞,我当然不想改变传递的参数。

如何迭代我的字符串?

【问题讨论】:

  • 在函数末尾省略 return 是惯用的生锈。只需写max_width(不带分号)。

标签: rust


【解决方案1】:

您的代码失败,因为Iterator&amp;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

【讨论】:

  • 这行得通,但我很难理解语义。我知道Iterator 是一个特征(因此可以是实现该特征的任何东西),但我对impl 在这种情况下的关键字感到困惑。当我们以前没有已知大小时,这怎么会导致我们拥有已知大小?
  • 我推荐阅读this。基本上和fn max_width&lt;'a, T: Iterator&lt;Item = &amp;'a String&gt;(strings: T) -&gt; usize一样。
  • 哦!所以Iterator 是特征本身,而impl Iterator 是实现该特征的东西。非常感谢!!
  • 谢谢@Tom。我认为this section 匹配旧链接。
【解决方案2】:

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

为什么我不能使用 &amp;Iterator&lt;Item = &amp;String&gt; 作为迭代器?

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

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

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

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 接受任何可以制成迭代器的东西。

【讨论】:

  • 我更喜欢这个答案,因为它还提供了一个接近 OP 最初意图的解决方案,使用动态多态性。
【解决方案3】:

如果您并不特别需要迭代任何给定迭代器的通用性,编写函数的更简单方法是让您的max_width 函数采用&amp;[&amp;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

【讨论】:

  • 谢谢,我会更新的。 &amp;String 是否也被引用到 &amp;str 并且您不会通过它吗?我想也许 OP 刚接触 Rust 只是想迭代一堆字符串,而 Vec 似乎是一种简单的方法。不过,通用解决方案很有趣。
  • 抱歉,我应该添加我的用例!我正在从结构向量 (max_width(recipes.iter().map(|x| &amp;x.name))) 中解压缩字段,所以我已经有了一个迭代器,因此函数使用它而不是向量。
  • @pjf 如果你可以考虑用这个替换它:recipes.iter().map(|x| x.name.len()).max().unwrap_or(0)
  • 您也可以将您的用例添加到问题中:)
猜你喜欢
  • 1970-01-01
  • 2020-09-22
  • 2021-10-27
  • 2020-07-23
  • 2011-02-05
  • 2020-05-01
  • 1970-01-01
  • 2014-01-15
  • 1970-01-01
相关资源
最近更新 更多