从 &str 数组中获取 Iterator<Item=str>?

Getting an Iterator<Item=str> from an array of &str?

我正在尝试抽象一个函数以获取 std::str::Lines 的实例和用于测试目的的模拟版本,该模拟版本由 &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 returns 一个 &T,在这种情况下会产生一个 &&str。我不明白的是为什么删除 map 并将 iter 替换为 into_iter(即 let v = ["3", "3", "4", "-2", "-4"].into_iter();)也会失败并出现相同的错误!

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

在编写此 post 时,我还尝试用 Vec 替换数组并使用 into_iter,这样最终结果是 let v = vec!["3","3","4","-2","-4"].into_iter(); 并且成功了!但是,现在我更加困惑了,为什么 into_iterVec 有效,但对 Array 无效?

在这种情况下,您可以将任何可以作为引用的内容的迭代器带入 str(任何 &&&&&..&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 适用于 Vec 而不是切片,好吧,如果您在使用 into_iter 时阅读当前警告:

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; }); Currently this works, as into_iter returns an iterator over references to the array's values, meaning that n is indeed &{integer} and can be dereferenced.

这是在 Rust 1.53 release notes 中宣布的。 IntoIterator for arrays 是在 1.53 中实现的新增功能,但在 2018 和 2021 版本中表现不同:

This was not implemented before, due to backwards compatibility problems. Because IntoIterator was already implemented for references to arrays, array.into_iter() already compiled in earlier versions, resolving to (&array).into_iter().

As of this release, arrays implement IntoIterator with a small workaround to avoid breaking code. The compiler will continue to resolve array.into_iter() to (&array).into_iter(), as if the trait implementation does not exist. This only applies to the .into_iter() method call syntax, and does not affect any other syntax such as for e in [1, 2, 3], iter.zip([1, 2, 3]) or IntoIterator::into_iter([1, 2, 3]), which all compile fine.

Since this special case for .into_iter() is only required to avoid breaking existing code, it is removed in the new edition, Rust 2021, which will be released later this year. See the edition announcement for more information.

因此您的代码将 compile just fine 与 Rust 2021