繁体   English   中英

如何展平 Result 的迭代器<Vec<T> ,E&gt; 返回 Result 的迭代器<T,E>生锈?

[英]How do I flatten an iterator of Result<Vec<T>,E> to return an iterator of Result<T,E> in rust?

这是我的测试用例; 由于不兼容的匹配臂,它无法编译。 我应该如何解决这个问题,或者在这里获得扁平迭代器的更好方法是什么?

fn main() {
    let v1: Vec<Result<Vec<u32>, &str>> = vec![ Ok(vec![1,2,3]), Ok(vec![4,5,6,9]), Err("failed"), ];

    let iter1 = v1.iter();

    let flattened_iter1 = iter1.map(|x| match x {
        Ok(v) => v.iter().map(|v| Ok(v)),
        Err(e) => once(Err(e)),
    }).flatten();

    for r in flattened_iter1 {
        match r {
            Ok(i) => println!("{}", i),
            Err(i) => { println!("Error"); break },
        }
    }
}

一种选择是使用提供Either<L, R>枚举的 Either either 一个Either<L, R>可以是Either::Left(L)Either::Right(R) ,当LR都用相同的Iterator::Item实现Iterator时, Either<L, R>实现Iterator本身类型。

这为您提供了与盒装特征对象类似的灵活性,但没有堆分配的开销。 仍然存在动态调度,但由Either在内部作为条件而不是函数指针调用处理。

use std::iter::once;
use either::Either;

fn main() {
    let v1: Vec<Result<Vec<u32>, &str>> = vec![ Ok(vec![1,2,3]), Ok(vec![4,5,6,9]), Err("failed"), ];

    let iter1 = v1.iter();

    let flattened_iter1 = iter1.map(|x| match x {
        Ok(v) => Either::Left(v.iter().map(|v| Ok(v))),
        Err(e) => Either::Right(once(Err(e))),
    }).flatten();

    for r in flattened_iter1 {
        match r {
            Ok(i) => println!("{}", i),
            Err(i) => { println!("Error"); break },
        }
    }
}

游乐场

这是行不通的,因为maponce会产生两种不同的类型。 你想要的方式是告诉 Rust 你并不真正关心它们是不同的类型,你唯一关心的是它们都是迭代器。 为此,您可以将它们显式地转换为特征对象once(Err(e)) as dyn Iterator<Item=Result<u32, &str>> ,但这不起作用,因为 Rust 在编译时无法知道它们的大小时间。 为了使其工作,您需要将它们分配在堆上而不是堆栈上,从而导致

use std::iter::once;

fn main() {
    let v1 = vec![ Ok(vec![1,2,3]), Ok(vec![4,5,6,9]), Err("failed"), ];

    let flattened_iter1 = v1
        .iter()
        .map(|x| match x {
            Ok(v) => Box::new(v.iter().map(|v| Ok(v))) as Box<dyn Iterator<Item=_>>,
            Err(e) => Box::new(once(Err(e))) as Box<dyn Iterator<Item=_>>,
        })
        .flatten();

    for r in flattened_iter1 {
        match r {
            Ok(i) => println!("{}", i),
            Err(i) => { println!("Error"); break },
        }
    }
}

你可以在操场上试试这个。

这种方法有几点需要注意:

  • Box的东西不是免费的,因为它会导致实际分配,但它也不是超级昂贵。 与您将所有内容收集到单个Vec以对其进行迭代的解决方案相比,这可能更便宜;
  • 同样,使用 trait 对象也不是免费的(因为他们必须带上自己的 vtable 和所有东西),但也不是很昂贵。
  • 我不必指定实际的迭代器Item ,因为 Rust 能够自己找出它。

虽然在一般情况下我会使用either一板条箱,但在这种特定情况下, itertools已经让您使用flatten_ok()

use itertools::Itertools;

let flattened_iter1 = iter1.map(Result::as_ref).flatten_ok();

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM