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

时间:2019-06-07 18:01:07

标签: 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])");

下一个相同,但声明输出的格式略有不同:

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

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

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

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

我不明白除法结果的结果类型是什么。我尝试使用编译器错误消息以及the answer of this question进行查找,但是结果对我而言是不透明的,这可能是由于延迟评估造成的。例如,仅将x替换为divide_results,以便assert将显示类型,如下所示:

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

给我这个结果:

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

由于迭代和映射尚未发生,因此尚不清楚左侧结果是什么。我尝试过的其他各种方法都能提供类似的结果。

考虑到问题是延迟评估,我还尝试使用collect来查看是否会强制评估。例如,在divide_results行的结尾处调用collect,如下所示:

division_results = numbers.into_iter().map(|n| divide(n, 27)).collect();

提供错误:

cannot infer type consider giving `division_results` a type

当我修改收集说:

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

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

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>>`

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

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

仅会收到此错误:

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>`

很明显,我缺少了一些东西。也许你能告诉我什么?

1 个答案:

答案 0 :(得分:1)

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

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

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

x

这使用了implementation of the FromIterator trait that allows to collect an iterable of Results into a Result wrapping a collection of values

您提到的另一种情况非常相似。这次,目标类型是let x: Result<Vec<i32>, DivisionError> = division_results.collect(); 实例的向量,因此您所需要做的就是指定其他类型。这将自动为您选择Result的正确实现:

FromIterator