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

问题描述 投票:10回答:4

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

rust
4个回答
15
投票

最直接的解决方案是使用对特征的引用] ::

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时最有用。占用的堆栈空间是单个指针,并且只会分配所需的堆空间。

您也可以use an enum for each possible concrete iterator


11
投票

either crate提供Either类型。如果Either的两个半都是迭代器,则Either也是:

Either

11
投票

个人,而不是使用futures::Either,我通常更喜欢创建一系列链接在一​​起的futures::Either值。像这样的东西:


7
投票

这里是@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表达式,这在处理更多条件情况时可能更方便:

© www.soinside.com 2019 - 2024. All rights reserved.