问题描述
我正在尝试在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 Token
s:
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`?
这篇关于如何返回一个结果,其中包含结果迭代器中的每个错误,而不仅仅是第一个错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!