如何确定 Rust 迭代器的结果类型?

Rus*_*Bob 2 rust

我正在通过rustlings练习来学习 Rust。我已经达到了iterator3.rs练习并且被卡住了。这个练习要求我提供一行代码,作为操作的一部分,将结果从一种类型映射到另一种类型;我需要用正确的操作填写 x= 行。有两部分 - 第一部分阅读:

let numbers = vec![27, 297, 38502, 81];
let division_results = numbers.into_iter().map(|n| divide(n, 27));
let x = ???
assert_eq!(format!("{:?}", x), "Ok([1, 11, 1426, 3])");
Run Code Online (Sandbox Code Playgroud)

下一个是相同的,但输出断言的格式略有不同:

assert_eq!(format!("{:?}", x), "[Ok(1), Ok(11), Ok(1426), Ok(3)]");
Run Code Online (Sandbox Code Playgroud)

我相信我明白第一个实例需要返回一个包含 i32 向量或某种错误类型的结果。第二个需要返回一个结果向量,每个结果都有一个 i32 或一个错误类型。

但是,我通常难以理解如何确定 into_iter、map 和 collect 的组合返回的类型。我可以使用一些帮助来学习如何推理或获得编译器帮助。

这是我到目前为止的位置:

我不明白 Division_results 的结果类型是什么。我曾尝试使用编译器错误消息以及此问题的答案来查找,但结果对我来说是不透明的,可能是因为懒惰的评估。例如,只需将 x 替换为 Division_results,这样 assert 就会显示类型,如下所示:

assert_eq!(format!("{:?}", division_results), "Ok([1, 11, 1426, 3])");
Run Code Online (Sandbox Code Playgroud)

给我这个结果:

left: `"Map { iter: IntoIter([27, 297, 38502, 81]) }"`,
right: `"Ok([1, 11, 1426, 3])"`', exercises/standard_library_types/iterator3.rs:75:9
Run Code Online (Sandbox Code Playgroud)

由于迭代和地图没有发生,所以不清楚左边的结果是什么。我尝试过的其他各种事情提供了类似的结果。

认为问题是懒惰的评估,我也尝试过使用 collect 看看是否会强制评估。例如,在 Division_results 行的末尾调用 collect 像这样:

division_results = numbers.into_iter().map(|n| divide(n, 27)).collect();
Run Code Online (Sandbox Code Playgroud)

提供一个错误:

cannot infer type consider giving `division_results` a type
Run Code Online (Sandbox Code Playgroud)

当我修改 collect 说:

let division_results = numbers.into_iter().map(|n| divide(n, 27)).collect::<i32>();
Run Code Online (Sandbox Code Playgroud)

我收到一个错误,我认为它给了我正确类型的提示:

let division_results = numbers.into_iter().map(|n| divide(n, 27)).collect::<i32>();
   |                                                                           ^^^^^^^ a collection of type `i32` cannot be built from `std::iter::Iterator<Item=std::result::Result<i32, DivisionError>>`
Run Code Online (Sandbox Code Playgroud)

所以我尝试使用错误消息中显示的类型:

let division_results = numbers.into_iter().map(|n| divide(n, 27)).collect::<Result<i32, DivisionError>>();
Run Code Online (Sandbox Code Playgroud)

只是为了得到这个错误:

let division_results = numbers.into_iter().map(|n| divide(n, 27)).collect::<Result<i32, DivisionError>>();
   |                                                                           ^^^^^^^ a collection of type `i32` cannot be built from `std::iter::Iterator<Item=i32>`
Run Code Online (Sandbox Code Playgroud)

显然我错过了一些东西。也许你能告诉我什么?

Sve*_*ach 5

所述map()的方法上迭代器适配器; 它接受一个迭代器,并返回另一个迭代器,但它本身不消耗原始迭代器中的任何项目。返回类型Map是原始迭代器的包装器,当它们被消费时,它将提供的闭包应用于每个项目。

如果您希望Map实际执行某些操作,则需要使用迭代器。最常见的方法是for循环和collect()方法(但还有许多其他方法使用迭代器,如sum(), count(), fold(), max(), ...)。在这种特殊情况下,调用该collect()方法是最合适的,因为您希望将结果收集到一个向量中。

您已经发现所需的类型xResult包装向量i32或错误,或Result<Vec<i32>, DivisionError>在 Rust 语法中。由于collect()可以产生许多不同的返回类型,我们需要告诉编译器我们想要哪一种。一种方法是明确指定 的类型x

let x: Result<Vec<i32>, DivisionError> = division_results.collect();
Run Code Online (Sandbox Code Playgroud)

这使用了trait实现,FromIterator它允许将Results的可迭代对象收集到一个Result包装值集合中

你提到的另一种情况非常相似。这一次,目标类型是一个Result实例向量,因此您需要做的就是指定不同的类型。这将自动FromIterator为您选择正确的实现:

let x: Vec<Result<i32, DivisionError>> = division_results.collect();
Run Code Online (Sandbox Code Playgroud)