我正在尝试根据输入到函数的Option
来切换行为。想法是基于是否存在给定的Option
进行迭代。这是一个最小的,如果很愚蠢的例子:
use std::iter;
fn main() {
let x: Option<i64> = None;
// Repeat x 5 times if present, otherwise count from 1 to 5
for i in match x {
None => 1..5,
Some(x) => iter::repeat(x).take(5),
} {
println!("{}", i);
}
}
我收到一个错误:
error[E0308]: match arms have incompatible types
--> src/main.rs:7:14
|
7 | for i in match x {
| ______________^
8 | | None => 1..5,
9 | | Some(x) => iter::repeat(x).take(5),
| | ----------------------- match arm with an incompatible type
10 | | } {
| |_____^ expected struct `std::ops::Range`, found struct `std::iter::Take`
|
= note: expected type `std::ops::Range<{integer}>`
found type `std::iter::Take<std::iter::Repeat<i64>>`
当然,这完全有道理,但是我真的很想根据条件选择迭代器,因为for循环中的代码很简单,复制所有这些代码只是为了更改迭代器选择,非常难看且难以维护。
我试图在两臂上都使用as Iterator<Item = i64>
,但这给我关于未定型类型的错误,因为它是一个特征对象。有没有简单的方法可以解决此问题?
我当然可以使用.collect()
,因为它们返回相同的类型并遍历该向量。这是一个很好的快速解决方案,但对于大型列表似乎有点过分。
最直接的解决方案是使用对特征的引用] ::
use std::iter; fn main() { let mut a; let mut b; let x: Option<i64> = None; // Repeat x 5 times if present, otherwise count from 1 to 5 let iter: &mut Iterator<Item = i64> = match x { None => { a = 1..5; &mut a } Some(x) => { b = iter::repeat(x).take(5); &mut b } }; for i in iter { println!("{}", i); } }
此解决方案的主要缺点是,您必须为每种具体类型分配堆栈空间。这也意味着每种类型的变量。一件好事是只需要初始化所使用的类型。
相同的想法但需要分配堆是使用装箱的特征对象
:use std::iter; fn main() { let x: Option<i64> = None; // Repeat x 5 times if present, otherwise count from 1 to 5 let iter: Box<Iterator<Item = i64>> = match x { None => Box::new(1..5), Some(x) => Box::new(iter::repeat(x).take(5)), }; for i in iter { println!("{}", i); } }
这在您想要return the iterator from a function时最有用。占用的堆栈空间是单个指针,并且只会分配所需的堆空间。
either crate提供Either
类型。如果Either
的两个半都是迭代器,则Either
也是:
Either
个人,而不是使用futures::Either
,我通常更喜欢创建一系列链接在一起的futures::Either
值。像这样的东西:
这里是@Niko出色解决方案的一种变体,它使用单个use std::iter;
fn main() {
let x: Option<i64> = None;
// Repeat x 5 times if present, otherwise count from 1 to 5
for i in pick(x) {
println!("{}", i);
}
}
fn pick(opt_x: Option<i64>) -> impl Iterator<Item = i64> {
let iter_a = if let None = opt_x {
Some(1..5)
} else {
None
};
let iter_b = if let Some(x) = opt_x {
Some(iter::repeat(x).take(5))
} else {
None
};
iter_a.into_iter().flatten().chain(iter_b.into_iter().flatten())
}
表达式而不是几个Either
表达式,这在处理更多条件情况时可能更方便: