【问题标题】:Getting an Iterator<Item=str> from an array of &str?从 &str 数组中获取 Iterator<Item=str>?
【发布时间】:2021-09-06 14:46:32
【问题描述】:

我正在尝试抽象一个函数来获取std::str::Lines 的实例和用于测试目的的模拟版本,由&amp;str 的数组创建。

我的代码(确实有效)如下所示:

use std::fs;

#[test]
fn test_day_1() {
    let v = ["3", "3", "4", "-2", "-4"].iter().map(|x| *x);
    assert_eq!(day1(v), "334-2-4334-2-4");
}

fn day1_pre() -> String {
    let contents = fs::read_to_string("day1.txt").expect("error reading file");
    day1(contents.lines())
}

fn day1<'a>(lines: impl Iterator<Item = &'a str> + Clone) -> String {
    lines
        .map(|line| {
            let v: Result<i32, _> = line.parse();
            v.expect("could not parse line as integer")
        })
        .cycle()
        .take(10)
        .map(|x| x.to_string())
        .collect()
}

然而,这段代码之所以有效,是因为测试中有奇怪的.map(|x| *x)。如果我删除它,我会收到以下错误:

error[E0271]: type mismatch resolving `<std::slice::Iter<'_, &str> as Iterator>::Item == &str`
  --> src/lib.rs:6:16
   |
6  |     assert_eq!(day1(v), "334-2-4334-2-4");
   |                ^^^^ expected `str`, found `&str`
...
14 | fn day1<'a>(lines: impl Iterator<Item = &'a str> + Clone) -> String {
   |                                  -------------- required by this bound in `day1`
   |
   = note: expected reference `&str`
              found reference `&&str`

我有点理解错误。 iter 返回一个&amp;T,在这种情况下产生一个&amp;&amp;str。我不明白的是为什么删除map 并用into_iter(即let v = ["3", "3", "4", "-2", "-4"].into_iter();)替换iter 也会失败并出现同样的错误!

根据the documentationinto_iter 迭代T,因此它应该在这里工作?

在写这篇文章时,我还尝试用Vec 替换数组并使用into_iter,这样最终的结果就是let v = vec!["3","3","4","-2","-4"].into_iter();,并且成功了!但是,现在我更困惑了,为什么into_iter 可以为Vec 工作,而不能为Array 工作?

【问题讨论】:

标签: rust iteration


【解决方案1】:

这是通过Rust 1.53 release notes 宣布的。数组的IntoIterator 是在 1.53 中实现的新增功能,但在 2018 和 2021 版本中的行为有所不同:

由于向后兼容性问题,这之前没有实现。因为IntoIterator 已经实现了对数组的引用,所以array.into_iter() 已经在早期版本中编译,解析为(&amp;array).into_iter()

从这个版本开始,数组实现了 IntoIterator 一个小的解决方法,以避免破坏代码。编译器将继续将array.into_iter() 解析为(&amp;array).into_iter(),就好像特征实现不存在一样。这只适用于.into_iter()方法调用语法,不影响for e in [1, 2, 3]iter.zip([1, 2, 3])IntoIterator::into_iter([1, 2, 3])等其他任何语法,都可以正常编译。

由于 .into_iter() 的这种特殊情况只是为了避免破坏现有代码,因此在今年晚些时候发布的新版本 Rust 2021 中将其删除。有关详细信息,请参阅版本公告。

因此,您的代码将使用 Rust 2021 compile just fine

【讨论】:

    【解决方案2】:

    在这种情况下,您可以将任何可以作为引用的迭代器放入 str 中(任何 &amp;&amp;&amp;&amp;&amp;..&amp;str 都应该):

    fn day1<T>(lines: impl Iterator<Item = T> + Clone) -> String where T : AsRef<str>{
        lines
            .map(|line| {
                let v: Result<i32, _> = line.as_ref().parse();
                v.expect("could not parse line as integer")
            })
            .cycle()
            .take(10)
            .map(|x| x.to_string())
            .collect()
    }
    

    Playground

    如果您在使用into_iter 时阅读当前警告,为什么into_iter 会为Vec 而不是切片工作:

    warning: this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` (due to autoref coercions), but that might change in the future when `IntoIterator` impls for arrays are added.
     --> src/lib.rs:5:41
      |
    5 |     let v = ["3", "3", "4", "-2", "-4"].into_iter();
      |                                         ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
      |
      = note: `#[warn(array_into_iter)]` on by default
      = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
      = note: for more information, see issue #66145 <https://github.com/rust-lang/rust/issues/66145>
    

    关注issue link,那里有很好的解释。 重点是:

    [1, 2, 3].into_iter().for_each(|n| { *n; }); 目前这个工作, 因为 into_iter 在对数组的引用上返回一个迭代器 值,这意味着 n 确实是 &{integer} 并且可以取消引用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-01-13
      • 2018-04-19
      • 1970-01-01
      • 1970-01-01
      • 2022-10-13
      • 1970-01-01
      • 2011-09-05
      • 1970-01-01
      相关资源
      最近更新 更多