有条件地迭代几个可能的迭代器之一
Conditionally iterate over one of several possible iterators
我正在尝试根据对函数的 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()
,因为它们 return 具有相同的类型并遍历该向量。这是一个很好的快速修复,但对于大型列表来说似乎有点过分。
最直接的解决方案是使用 trait 对象:
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 dyn 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);
}
}
此解决方案的主要缺点是您必须为您拥有的每个具体类型分配堆栈 space。这也意味着每种类型的变量。好处是只需要初始化使用过的类型。
相同的想法但需要堆分配是使用盒装特征对象:
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<dyn 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 时非常有用。取的栈space是单指针,只会分配需要的堆space。
你也可以.
either crate provides the Either
类型。如果 Either
的两半都是迭代器,那么 Either
:
也是
extern crate either;
use either::Either;
use std::iter;
fn main() {
let x: Option<i64> = None;
// Repeat x 5 times if present, otherwise count from 1 to 5
let iter = match x {
None => Either::Left(1..5),
Some(x) => Either::Right(iter::repeat(x).take(5)),
};
for i in iter {
println!("{}", i);
}
}
与 一样,对于您拥有的每个具体类型,这仍然需要堆栈 space。但是,您不需要为每个具体值设置单独的变量。
这个类型也可以是returned from a function,不像trait对象引用。与盒装特征对象相比,无论选择哪种具体类型,它都将始终在堆栈上使用固定大小。
您也会在其他地方找到这种类型(或语义等价物),例如 futures::Either
就个人而言,与其使用 Either
,我更喜欢创建一系列链接在一起的 Option<Iterator>
值。像这样:
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
那么明显,但它避免了另一个板条箱,有时它的效果非常优雅。
这是@Niko 的绝妙解决方案的变体,它使用单个 match
表达式而不是多个 if let
表达式,在处理更多条件情况时可能更方便:
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 mut iter_a = None;
let mut iter_b = None;
match opt_x {
None => iter_a = Some(1..5),
Some(x) => iter_b = Some(iter::repeat(x).take(5)),
}
iter_a.into_iter().flatten().chain(iter_b.into_iter().flatten())
}
我正在尝试根据对函数的 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()
,因为它们 return 具有相同的类型并遍历该向量。这是一个很好的快速修复,但对于大型列表来说似乎有点过分。
最直接的解决方案是使用 trait 对象:
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 dyn 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);
}
}
此解决方案的主要缺点是您必须为您拥有的每个具体类型分配堆栈 space。这也意味着每种类型的变量。好处是只需要初始化使用过的类型。
相同的想法但需要堆分配是使用盒装特征对象:
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<dyn 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 时非常有用。取的栈space是单指针,只会分配需要的堆space。
你也可以
either crate provides the Either
类型。如果 Either
的两半都是迭代器,那么 Either
:
extern crate either;
use either::Either;
use std::iter;
fn main() {
let x: Option<i64> = None;
// Repeat x 5 times if present, otherwise count from 1 to 5
let iter = match x {
None => Either::Left(1..5),
Some(x) => Either::Right(iter::repeat(x).take(5)),
};
for i in iter {
println!("{}", i);
}
}
与
这个类型也可以是returned from a function,不像trait对象引用。与盒装特征对象相比,无论选择哪种具体类型,它都将始终在堆栈上使用固定大小。
您也会在其他地方找到这种类型(或语义等价物),例如 futures::Either
就个人而言,与其使用 Either
,我更喜欢创建一系列链接在一起的 Option<Iterator>
值。像这样:
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
那么明显,但它避免了另一个板条箱,有时它的效果非常优雅。
这是@Niko 的绝妙解决方案的变体,它使用单个 match
表达式而不是多个 if let
表达式,在处理更多条件情况时可能更方便:
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 mut iter_a = None;
let mut iter_b = None;
match opt_x {
None => iter_a = Some(1..5),
Some(x) => iter_b = Some(iter::repeat(x).take(5)),
}
iter_a.into_iter().flatten().chain(iter_b.into_iter().flatten())
}