Idiomati c способ собрать все ошибки от итератора - PullRequest
4 голосов
/ 11 апреля 2020

Допустим, у меня есть 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 }));
}

Эта вторая версия делает то, что я хочу, но значительно более многословна, чем первая версия. Есть ли лучший / более идиоматический c способ добиться этого, если это возможно, без создания собственного итератора?

Ответы [ 2 ]

1 голос
/ 12 апреля 2020

Насколько мне известно, в стандартной библиотеке нет удобной однострочной строки, но отличная библиотека 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);
}

( Постоянная ссылка на игровую площадку )

0 голосов
/ 12 апреля 2020

В итоге я написал собственное расширение для 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) }
            }
        })
    }
}
...