而不仅仅是第一个错误

而不仅仅是第一个错误

本文介绍了如何返回一个结果,其中包含结果迭代器中的每个错误,而不仅仅是第一个错误?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在Rust中实现一个简单的解释器,为此我创建了一个 Tokens 结构,该结构接受源字符并生成一个令牌 Result 内的 ScanError

I'm trying to implement a simple interpreter in Rust, for which I have created a Tokens struct, which takes source characters and produces either a Token or a ScanError inside a Result:

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 ,很容易将结果收集到第一个 ScanError Token s的向量:

Since Result implements FromIterator, it is simple to collect the result to either the first ScanError or a vector of Tokens:

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

    iter.collect()
}

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

In the case of multiple errors I really want to return every error:

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

据我所知,不可能实现自己的版本 FromIterator 是因为该特征或 Result 都不是我的板条箱本地的。有人可以建议一种干净的方法吗?

It isn't possible as far as I know to implement my own version of FromIterator because neither that trait or Result are local to my crate. Can anyone suggest a clean way of doing this?

我在迭代器上使用 partition 编写了一个实现,然后展开下面的每个结果,但是阅读起来并不有趣,并且感觉不太好使用迭代器:

I have written an implementation using partition on the iterator, then unwrapping each Result, below, but it's not fun to read and doesn't feel like good use of iterators:

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


推荐答案

我会使用itertools的避免需要拆包:

I would use itertools' partition_map to avoid the need to unwrap:

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

另请参见:





  • What's the most idiomatic way of working with an Iterator of Results?
  • How do I stop iteration and return an error when Iterator::map returns a Result::Err?
  • How do I perform iterator computations over iterators of Results without collecting to a temporary vector?

您还可以使用 Option Result 实施 IntoIterator 可以避免确切的 unwrap 的使用,尽管它仍然处理一个集合两次:

You could also use the fact that Option and Result implement IntoIterator to avoid the exact unwrap, although this still processes one collection twice:

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

另请参见:



  • Why does `Option` support `IntoIterator`?

这篇关于如何返回一个结果,其中包含结果迭代器中的每个错误,而不仅仅是第一个错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-25 13:43