有条件地迭代几个可能的迭代器之一 [英] Conditionally iterate over one of several possible iterators

查看:47
本文介绍了有条件地迭代几个可能的迭代器之一的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试根据函数的 Option 输入来切换行为.这个想法是根据给定的 Option 是否存在进行迭代.这是一个最小的,如果愚蠢的例子:

I'm trying to switch behavior based on an Option input to a function. The idea is to iterate based on whether or not a given Option is present. Here's a minimal, if silly, example:

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 循环中的代码非常重要并且复制粘贴所有这些只是为了更改迭代器选择会非常丑陋且无法维护.

This makes perfect sense, of course, but I'd really like to choose my iterator based on a condition, since the code in the for-loop is non-trivial and copy-pasting all of that just to change iterator selection would be pretty ugly and unmaintainable.

我尝试在双臂上使用 作为 Iterator ,但这给了我一个关于 unsized 类型的错误,因为它是一个 trait 对象.有没有简单的方法来解决这个问题?

I tried using as Iterator<Item = i64> on both arms, but that gives me an error about unsized types because it's a trait object. Is there an easy way to go about this?

我当然可以使用 .collect() 因为它们返回相同的类型并迭代该向量.这是一个很好的快速解决方案,但对于大型列表来说似乎有点过分.

I could, of course, use .collect() since they return the same type and iterate over that vector. Which is a good quick fix, but for large lists seems a bit excessive.

推荐答案

最直接的解决方案是使用 trait 对象:

The most straightforward solution is to use a trait object:

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);
    }
}

此解决方案的主要缺点是您必须为您拥有的每种具体类型分配堆栈空间.这也意味着每种类型的变量.好在只需要初始化使用的类型.

The main downside for this solution is that you have to allocate stack space for each concrete type you have. This also means variables for each type. A good thing is that only the used type needs to be initialized.

同样的想法但需要堆分配是使用盒装特征对象:

The same idea but requiring heap allocation is to use boxed trait objects:

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);
    }
}

当您想从函数返回迭代器时,这非常有用.占用的栈空间是单个指针,只分配需要的堆空间.

This is mostly useful when you want to return the iterator from a function. The stack space taken is a single pointer, and only the needed heap space will be allocated.

您还可以为每个可能的具体迭代器使用枚举.

这篇关于有条件地迭代几个可能的迭代器之一的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆