如何从结果的迭代器返回包含每个错误的Result,而不仅仅是第一个?

问题描述 投票:2回答:2

我正在尝试在Rust中实现一个简单的解释器,为此我创建了一个Tokens结构,它接受源字符并在Token中生成ScanErrorResult

pub struct Tokens<'src> {
    chars: Chars<'src>,
}

impl<'src> Iterator for Tokens<'src> {
    type Item = Result<Token, ScanError>;

    fn next(&mut self) -> Option<Result<Token, ScanError>> {
        //  ...
    }
}

由于Result实现FromIterator,将结果收集到第一个ScanErrorTokens矢量很简单:

fn scan_tokens(source: &str) -> Result<Vec<Token>, ScanError> {
    let iter = Tokens {
        chars: source.chars(),
    };

    iter.collect()
}

在多个错误的情况下,我真的想要返回每个错误:

fn scan_tokens(source: &str) -> Result<Vec<Token>, Vec<ScanError>> {
    // what goes here?
}

据我所知,实现我自己的FromIterator版本是不可能的,因为这个特性或Result都不是我的箱子。任何人都可以建议一个干净的方式吗?

我在迭代器上编写了一个使用partition的实现,然后在下面展开每个Result,但是读取它并不好用,并且不喜欢使用迭代器:

type T = Vec<Result<Token, ScanError>>;
fn scan_tokens(source: &str) -> Result<Vec<Token>, Vec<ScanError>> {
    let iter = Tokens {
        chars: source.chars(),
    };

    let (tokens_results, error_results): (T, T) = iter.partition(|result| result.is_ok());
    let errors: Vec<ScanError> = error_results
        .into_iter()
        .map(|result| result.unwrap_err())
        .collect();

    if errors.len() > 0 {
        return Err(errors);
    }

    Ok(tokens_results
        .into_iter()
        .map(|result| result.unwrap())
        .collect())
}
error-handling rust iterator
2个回答
4
投票

打开每个Result

我会使用itertools的partition_map来避免需要打开:

use itertools::{Either, Itertools}; // 0.8.0

fn iterator() -> impl Iterator<Item = Result<i32, bool>> {
    vec![Ok(1), Err(false), Ok(2), Err(true), Ok(3)].into_iter()
}

fn example() -> Result<Vec<i32>, Vec<bool>> {
    let (values, errors): (Vec<_>, Vec<_>) = iterator().partition_map(|v| match v {
        Ok(v) => Either::Left(v),
        Err(e) => Either::Right(e),
    });

    if errors.is_empty() {
        Ok(values)
    } else {
        Err(errors)
    }
}

也可以看看:

您也可以使用OptionResult实现IntoIterator以避免确切的unwrap这一事实,尽管这仍然处理一个集合两次:

fn example2() -> Result<Vec<i32>, Vec<bool>> {
    let (values, errors): (Vec<_>, Vec<_>) = iterator().partition(|result| result.is_ok());

    if errors.is_empty() {
        Ok(values.into_iter().flat_map(Result::ok).collect())
    } else {
        Err(errors.into_iter().flat_map(Result::err).collect())
    }
}

也可以看看:


1
投票

命令式解决方案通常是实现某种算法的最具表现力和最有效的方式。这是Rust,而不是Haskell;并非一切都需要功能。

fn scan_tokens(source: &str) -> Result<Vec<Token>, Vec<ScanError>> {
    let iter = Tokens {
        chars: source.chars(),
    };
    let mut tokens = Vec::new();
    let mut errors = Vec::new();
    for result in iter {
        match result {
            Ok(token) => {
                tokens.push(token);
            }
            Err(e) => {
                errors.push(e);
            }
        }
    }
    if errors.is_empty() {
        Ok(tokens)
    } else {
        Err(errors)
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.