了解在 Rust 中收集 `flat_map` 与收集 `map` 之间的细微差别

Understanding the nuances between collecting `flat_map` vs collecting `map` in rust

我正在使用 Advent of Code 学习 Rust。
要解析 day 04 2021,我需要解析以下字符串:

88 67 20 19 15
22 76 86 44 73
 7 42  6 69 25
12 68 92 21 75
97 45 13 52 70

75 98 24 18 77
17 93 46 49 13
92 56 97 57 66
44  0 65 54 74
23  6 53 42 20

92 94  9 27 41
73 28 62 90 40
78  3 12 37 32
 8 86 91 16 30
84 38 68 11 19

理想情况下,我想生成嵌套向量 Vec<Vec<&str>>
我的问题是我不明白为什么下面的代码不起作用。

let bingo_sheet = grids_str
  split("\r\n\r\n")
  .map(|grid| grid.split_whitespace())
  .collect::<Vec<Vec<&str>>>();

我也不明白cargo check的意思。 (好吧,这个特征不存在。但为什么呢?)

error[E0277]: a value of type `Vec<Vec<&str>>` cannot be built from an iterator over elements of type `SplitWhitespace<'_>`
  --> src\main.rs:36:10
   |
36 |         .collect::<Vec<Vec<&str>>>();
   |          ^^^^^^^ value of type `Vec<Vec<&str>>` cannot be built from `std::iter::Iterator<Item=SplitWhitespace<'_>>`
   |
   = help: the trait `FromIterator<SplitWhitespace<'_>>` is not implemented for `Vec<Vec<&str>>`

经过一些实验后,flat_map 的这种用法似乎有效。

let bingo_sheet = grids_str
   .split("\r\n\r\n")
   .flat_map(|grid| grid.split_whitespace())
   .collect::<Vec<&str>>();

我不明白这里发生了什么。
flat_map 代码的行为符合预期,但 map 代码并非如此。

在我写这篇文章的时候,这个问题已经在评论中得到了简短的回答,但也许更详细的版本仍然有帮助!

I also don't understand the meaning behind cargo check. (Ok the trait doesn't exist. But why?)

std 提供了经常使用的转换,但它并没有为您完成所有转换。

split_whitespace return 的调用是 SplitWhitespace struct that implements (in the rest of this I will simply say "is") Iterator。 对 map return 的调用是一个 Map 结构,它是其给定函数的 return 类型的迭代器,因此它是一个迭代器,一个迭代器,一个迭代器,一个字符串切片。

因为Map是一个迭代器,它有一个collect方法。正如您在文档中所见,如果存在实现 FromIterator<Self::Item>B,则此方法存在。这个 B 是你的 Vec<Vec<&str>>Self::ItemMap 的项目,SplitWhiteSpace 结构。所以 collect 是在问,“Vec<Vec<&str>> 实现了 FromIterator<SplitWhiteSpace> 吗?如果是,我存在,如果不存在,我拒绝存在”。

(FromIterator<SplitWhiteSpace> 并不意味着 Vec<T> 可以由 SplitWhiteSpace 结构构成,而是可以由某种类型 I 构成(或者可以被制作成)项目 SplitWhiteSpace 上的迭代器。)

所以让我们看看 Vec 文档,看看 Vec<Vec<&str>> 是否实现了 FromIterator<SplitWhiteSpace>Vec does implement FromIterator<T> but only for Vec<T>,因此在您的情况下它实现了 FromIterator<Vec<&str>>,但 collect 要求它实现 FromIterator<SplitWhiteSpace>。它不会,所以您的代码无法编译。

换句话说,将Map<X>收集到Vec<Y>时,X必须等于Y。

这就是在 .map 中添加 collect() 的原因。因为这使它成为 Map<Vec<&str>> ,这意味着类型匹配。 flatmap 也可以工作,因为它创建了一个 FlatMap<&str>,它的工作方式类似于 Map,并且可以收集到 Vec<&str> 中。 (flat_map 在其给定函数中不需要 collect,因为它可以处理其中的迭代器)