猿问

有条件地迭代几个可能的迭代器之一

我正在尝试根据功能的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()因为它们返回相同的类型并遍历该向量。这是一个很好的快速解决方案,但对于大型列表而言似乎有点多余。


红颜莎娜
浏览 543回答 3
3回答

慕虎7371278

的任一箱提供Either类型。如果两个Either都是迭代器,则Either:也是extern crate either;use either::Either;use std::iter;fn main() {&nbsp; &nbsp; let x: Option<i64> = None;&nbsp; &nbsp; // Repeat x 5 times if present, otherwise count from 1 to 5&nbsp; &nbsp; let iter = match x {&nbsp; &nbsp; &nbsp; &nbsp; None => Either::Left(1..5),&nbsp; &nbsp; &nbsp; &nbsp; Some(x) => Either::Right(iter::repeat(x).take(5)),&nbsp; &nbsp; };&nbsp; &nbsp; for i in iter {&nbsp; &nbsp; &nbsp; &nbsp; println!("{}", i);&nbsp; &nbsp; }}像先前的答案一样,这仍然会占用每种具体类型的堆栈空间。但是,您不需要为每个具体值使用单独的变量。也可以从函数返回此类型,这与trait对象引用不同。与盒装特征对象相比,无论选择哪种具体类型,它都将始终在堆栈上使用固定大小。您还会在其他地方找到这种类型(或语义等效),例如 futures::Either

潇湘沐

您需要引用一个特征:use std::iter;fn main() {&nbsp; &nbsp; let mut a;&nbsp; &nbsp; let mut b;&nbsp; &nbsp; let x: Option<i64> = None;&nbsp; &nbsp; // Repeat x 5 times if present, otherwise count from 1 to 5&nbsp; &nbsp; let iter: &mut Iterator<Item = i64> = match x {&nbsp; &nbsp; &nbsp; &nbsp; None => {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; a = 1..5;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &mut a&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; Some(x) => {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; b = iter::repeat(x).take(5);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &mut b&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; };&nbsp; &nbsp; for i in iter {&nbsp; &nbsp; &nbsp; &nbsp; println!("{}", i);&nbsp; &nbsp; }}该解决方案的主要缺点是,您必须为每种具体类型分配堆栈空间。这也意味着每种类型的变量。一件好事是只需要初始化使用的类型。相同的想法但需要堆分配的是使用装箱的特征对象:use std::iter;fn main() {&nbsp; &nbsp; let x: Option<i64> = None;&nbsp; &nbsp; // Repeat x 5 times if present, otherwise count from 1 to 5&nbsp; &nbsp; let iter: Box<Iterator<Item = i64>> = match x {&nbsp; &nbsp; &nbsp; &nbsp; None => Box::new(1..5),&nbsp; &nbsp; &nbsp; &nbsp; Some(x) => Box::new(iter::repeat(x).take(5)),&nbsp; &nbsp; };&nbsp; &nbsp; for i in iter {&nbsp; &nbsp; &nbsp; &nbsp; println!("{}", i);&nbsp; &nbsp; }}当您要从函数返回迭代器时,这最有用。占用的堆栈空间是单个指针,并且只会分配所需的堆空间。

波斯汪

就个人而言,Either我通常更喜欢创建一系列Option<Iterator>链接在一起的价值观,而不是使用。像这样:操场use std::iter;fn main() {&nbsp; &nbsp; let x: Option<i64> = None;&nbsp; &nbsp; // Repeat x 5 times if present, otherwise count from 1 to 5&nbsp; &nbsp; for i in pick(x) {&nbsp; &nbsp; &nbsp; &nbsp; println!("{}", i);&nbsp; &nbsp; }}fn pick(opt_x: Option<i64>) -> impl Iterator<Item = i64> {&nbsp; &nbsp; let iter_a = if let None = opt_x {&nbsp; &nbsp; &nbsp; &nbsp; Some(1..5)&nbsp;&nbsp;&nbsp; &nbsp; } else {&nbsp; &nbsp; &nbsp; &nbsp; None&nbsp; &nbsp; };&nbsp; &nbsp; let iter_b = if let Some(x) = opt_x {&nbsp; &nbsp; &nbsp; &nbsp; Some(iter::repeat(x).take(5))&nbsp; &nbsp; } else {&nbsp; &nbsp; &nbsp; &nbsp; None&nbsp; &nbsp; };&nbsp; &nbsp; iter_a.into_iter().flatten().chain(iter_b.into_iter().flatten())}与使用相比Either,它不那么明显,但是它避免了使用其他板条箱,有时效果非常好。
随时随地看视频慕课网APP
我要回答