鉴于foo
的定义:
let foo = vec![vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9]];
我希望能够编写如下代码:
let result: Vec<_> = foo.iter()
.enumerate()
.flat_map(|(i, row)| if i % 2 == 0 {
row.iter().map(|x| x * 2)
} else {
std::iter::empty()
})
.collect();
但是这引发了关于if和else子句具有不兼容类型的错误。我尝试暂时删除map
,我尝试在闭包之外定义一个空向量,然后像这样返回一个迭代器:
let empty = vec![];
let result: Vec<_> = foo.iter()
.enumerate()
.flat_map(|(i, row)| if i % 2 == 0 {
row.iter() //.map(|x| x * 2)
} else {
empty.iter()
})
.collect();
这似乎有些愚蠢,但它编译。如果我尝试取消注释map
,那么它仍会抱怨if和else子句具有不兼容的类型。以下是错误消息的一部分:
error[E0308]: if and else have incompatible types
--> src/main.rs:6:30
|
6 | .flat_map(|(i, row)| if i % 2 == 0 {
| ______________________________^
7 | | row.iter().map(|x| x * 2)
8 | | } else {
9 | | std::iter::empty()
10 | | })
| |_________^ expected struct `std::iter::Map`, found struct `std::iter::Empty`
|
= note: expected type `std::iter::Map<std::slice::Iter<'_, {integer}>, [closure@src/main.rs:7:28: 7:37]>`
found type `std::iter::Empty<_>`
我知道我可以用一些嵌套的for
循环编写一些可以做我想要的东西,但是我想知道是否有一种简洁的方法可以使用迭代器来编写它。
答案 0 :(得分:4)
由于Rust是静态类型的,并且迭代器链中的每个步骤都会将结果更改为包含先前类型的新类型(除非使用盒装特征对象),否则必须以两个分支都被覆盖的方式编写它相同的类型。
使用单一类型传达条件空性的一种方法是TakeWhile
迭代器实现。
.flat_map(|(i, row)| {
let iter = row.iter().map(|x| x * 2);
let take = i % 2 == 0;
iter.take_while(|_| take)
})
如果您不介意忽略输入迭代器foo
可能包含usize
个元素的边缘情况,您也可以使用Take
代替0或usize :: MAX。它的优势在于提供了比size_hint()
更好的TakeWhile
。
答案 1 :(得分:4)
在您的具体示例中,您可以在调用flat_map
之前使用filter
删除不需要的元素:
let result: Vec<_> = foo.iter()
.enumerate()
.filter(|&(i, _)| i % 2 == 0)
.flat_map(|(_, row)| row.iter().map(|x| x * 2))
.collect();
如果您希望将其与map
代替flat_map
一起使用,则可以使用filter_map将filter
和map
的调用合并到一起返回Option
的函数,仅保留Some(thing)
的元素。