4

假设我有attrs: Vec<Attribute>一些函数属性和一个fn map_attribute(attr: &Attribute) -> Result<TokenStream, Error>将属性映射到某些代码的函数。

我知道我可以写这样的东西:

attrs.into_iter()
     .map(map_attribute)
     .collect::<Result<Vec<_>, _>()?

然而,这不是我想要的。我想要的是一次吐出所有错误,而不是停止第一个错误。目前我做这样的事情:

let mut codes : Vec<TokenStream> = Vec::new();
let mut errors: Vec<Error>       = Vec::new();

for attr in attrs {
    match map_attribute(attr) {
        Ok(code) => codes.push(code),
        Err(err) => errors.push(err)
    }
}

let mut error_iter = errors.into_iter();
if let Some(first) = error_iter.nth(0) {
    return Err(iter.fold(first, |mut e0, e1| { e0.combine(e1); e0 }));
}

第二个版本可以满足我的要求,但比第一个版本要冗长得多。如果可能的话,是否有更好/更惯用的方法来实现这一点,而无需创建我自己的迭代器?

4

2 回答 2

1

据我所知,标准库没有一个方便的单线,但是优秀的itertools库有:

use itertools::Itertools; // 0.9.0

fn main() {
    let foo = vec![Ok(42), Err(":("), Ok(321), Err("oh noes")];

    let (codes, errors): (Vec<_>, Vec<_>)
        = foo.into_iter().partition_map(From::from);

    println!("codes={:?}", codes);
    println!("errors={:?}", errors);
}

永久链接到操场

于 2020-04-12T12:26:34.767 回答
0

我最终为 编写了自己的扩展程序Iterator,它允许我在遇到第一个错误时停止收集代码。这在我的用例中可能比mcarton的答案更有效,因为如果第二个分区为空,我只需要第一个分区桶。此外,如果我想将它们组合成一个错误,我无论如何都需要折叠错误。

pub trait CollectToResult
{
    type Item;

    fn collect_to_result(self) -> Result<Vec<Self::Item>, Error>;
}

impl<Item, I> CollectToResult for I
where
    I : Iterator<Item = Result<Item, Error>>
{
    type Item = Item;

    fn collect_to_result(self) -> Result<Vec<Item>, Error>
    {
        self.fold(<Result<Vec<Item>, Error>>::Ok(Vec::new()), |res, code| {
            match (code, res) {
                (Ok(code), Ok(mut codes)) => { codes.push(code); Ok(codes) },
                (Ok(_), Err(errors)) => Err(errors),
                (Err(err), Ok(_)) => Err(err),
                (Err(err), Err(mut errors)) => { errors.combine(err); Err(errors) }
            }
        })
    }
}
于 2020-04-12T16:31:00.873 回答