使用结果迭代器最惯用的方式是什么?
What's the most idiomatic way of working with an Iterator of Results?
我有这样的代码:
let things = vec![/* ...*/]; // e.g. Vec<String>
things
.map(|thing| {
let a = try!(do_stuff(thing));
Ok(other_stuff(a))
})
.filter(|thing_result| match *thing_result {
Err(e) => true,
Ok(a) => check(a),
})
.map(|thing_result| {
let a = try!(thing_result);
// do stuff
b
})
.collect::<Result<Vec<_>, _>>()
在语义上,我想在第一个错误后停止处理。
上面的代码可以,但是感觉比较繁琐。有没有更好的办法?我在文档中查看了类似 filter_if_ok
的内容,但我什么也没找到。
我知道 collect::<Result<Vec<_>, _>>
,而且效果很好。我特别想消除以下样板文件:
- 在过滤器的闭包中,我必须在
thing_result
上使用 match
。我觉得这应该只是一个单行,例如.filter_if_ok(|thing| check(a))
.
- 每次使用
map
时,我都必须包含一个额外的语句let a = try!(thing_result);
,以处理Err
的可能性。同样,我觉得这可以抽象为 .map_if_ok(|thing| ...)
.
我是否可以使用另一种方法来获得这种程度的简洁性,或者我只需要坚持下去?
filter_map
可用于减少映射然后过滤的简单情况。在您的示例中,过滤器有一些逻辑,所以我认为它不会简化事情。不幸的是,我在 Result
的文档中也没有看到任何有用的功能。我认为您的示例已尽其所能,但这里有一些小的改进:
let things = vec![...]; // e.g. Vec<String>
things.iter().map(|thing| {
// The ? operator can be used in place of try! in the nightly version of Rust
let a = do_stuff(thing)?;
Ok(other_stuff(a))
// The closure braces can be removed if the code is a single expression
}).filter(|thing_result| match *thing_result {
Err(e) => true,
Ok(a) => check(a),
}
).map(|thing_result| {
let a = thing_result?;
// do stuff
b
})
?
运算符在某些情况下可读性较差,因此您可能不想使用它。
如果您能够将 check
函数更改为 return Some(x)
而不是 true,并且 None
而不是 false,您可以使用 filter_map
:
let bar = things.iter().filter_map(|thing| {
match do_stuff(thing) {
Err(e) => Some(Err(e)),
Ok(a) => {
let x = other_stuff(a);
if check_2(x) {
Some(Ok(x))
} else {
None
}
}
}
}).map(|thing_result| {
let a = try!(thing_result);
// do stuff
b
}).collect::<Result<Vec<_>, _>>();
在某些情况下,您也可以通过使用匹配来摆脱 let a = try!(thing);
。但是,在这里使用 filter_map
似乎没有帮助。
您可以自己实现这些迭代器。查看 filter
and map
是如何在标准库中实现的。
map_ok
实施:
#[derive(Clone)]
pub struct MapOkIterator<I, F> {
iter: I,
f: F,
}
impl<A, B, E, I, F> Iterator for MapOkIterator<I, F>
where
F: FnMut(A) -> B,
I: Iterator<Item = Result<A, E>>,
{
type Item = Result<B, E>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(|x| x.map(&mut self.f))
}
}
pub trait MapOkTrait {
fn map_ok<F, A, B, E>(self, func: F) -> MapOkIterator<Self, F>
where
Self: Sized + Iterator<Item = Result<A, E>>,
F: FnMut(A) -> B,
{
MapOkIterator {
iter: self,
f: func,
}
}
}
impl<I, T, E> MapOkTrait for I
where
I: Sized + Iterator<Item = Result<T, E>>,
{
}
filter_ok
几乎一样:
#[derive(Clone)]
pub struct FilterOkIterator<I, P> {
iter: I,
predicate: P,
}
impl<I, P, A, E> Iterator for FilterOkIterator<I, P>
where
P: FnMut(&A) -> bool,
I: Iterator<Item = Result<A, E>>,
{
type Item = Result<A, E>;
#[inline]
fn next(&mut self) -> Option<Result<A, E>> {
for x in self.iter.by_ref() {
match x {
Ok(xx) => if (self.predicate)(&xx) {
return Some(Ok(xx));
},
Err(_) => return Some(x),
}
}
None
}
}
pub trait FilterOkTrait {
fn filter_ok<P, A, E>(self, predicate: P) -> FilterOkIterator<Self, P>
where
Self: Sized + Iterator<Item = Result<A, E>>,
P: FnMut(&A) -> bool,
{
FilterOkIterator {
iter: self,
predicate: predicate,
}
}
}
impl<I, T, E> FilterOkTrait for I
where
I: Sized + Iterator<Item = Result<T, E>>,
{
}
您的代码可能如下所示:
["1", "2", "3", "4"]
.iter()
.map(|x| x.parse::<u16>().map(|a| a + 10))
.filter_ok(|x| x % 2 == 0)
.map_ok(|x| x + 100)
.collect::<Result<Vec<_>, std::num::ParseIntError>>()
有很多方法可以表达这个意思。
如果你只是想恐慌,使用.map(|x| x.unwrap())
。
如果你想要所有结果或一个错误,collect
变成Result<X<T>>
:
let results: Result<Vec<i32>, _> = result_i32_iter.collect();
如果您想要除错误之外的所有内容,请使用 .filter_map(|x| x.ok())
或 .flat_map(|x| x)
。
如果您想要 直到 第一个错误的所有内容,请使用 .scan((), |_, x| x.ok())
.
let results: Vec<i32> = result_i32_iter.scan((), |_, x| x.ok());
请注意,在许多情况下,这些操作可以与之前的操作结合使用。
自 Rust 1.27 起,Iterator::try_for_each
可能会引起兴趣:
An iterator method that applies a fallible function to each item in the iterator, stopping at the first error and returning that error.
This can also be thought of as the fallible form of for_each()
or as the stateless version of try_fold()
.
我有这样的代码:
let things = vec![/* ...*/]; // e.g. Vec<String>
things
.map(|thing| {
let a = try!(do_stuff(thing));
Ok(other_stuff(a))
})
.filter(|thing_result| match *thing_result {
Err(e) => true,
Ok(a) => check(a),
})
.map(|thing_result| {
let a = try!(thing_result);
// do stuff
b
})
.collect::<Result<Vec<_>, _>>()
在语义上,我想在第一个错误后停止处理。
上面的代码可以,但是感觉比较繁琐。有没有更好的办法?我在文档中查看了类似 filter_if_ok
的内容,但我什么也没找到。
我知道 collect::<Result<Vec<_>, _>>
,而且效果很好。我特别想消除以下样板文件:
- 在过滤器的闭包中,我必须在
thing_result
上使用match
。我觉得这应该只是一个单行,例如.filter_if_ok(|thing| check(a))
. - 每次使用
map
时,我都必须包含一个额外的语句let a = try!(thing_result);
,以处理Err
的可能性。同样,我觉得这可以抽象为.map_if_ok(|thing| ...)
.
我是否可以使用另一种方法来获得这种程度的简洁性,或者我只需要坚持下去?
filter_map
可用于减少映射然后过滤的简单情况。在您的示例中,过滤器有一些逻辑,所以我认为它不会简化事情。不幸的是,我在 Result
的文档中也没有看到任何有用的功能。我认为您的示例已尽其所能,但这里有一些小的改进:
let things = vec![...]; // e.g. Vec<String>
things.iter().map(|thing| {
// The ? operator can be used in place of try! in the nightly version of Rust
let a = do_stuff(thing)?;
Ok(other_stuff(a))
// The closure braces can be removed if the code is a single expression
}).filter(|thing_result| match *thing_result {
Err(e) => true,
Ok(a) => check(a),
}
).map(|thing_result| {
let a = thing_result?;
// do stuff
b
})
?
运算符在某些情况下可读性较差,因此您可能不想使用它。
如果您能够将 check
函数更改为 return Some(x)
而不是 true,并且 None
而不是 false,您可以使用 filter_map
:
let bar = things.iter().filter_map(|thing| {
match do_stuff(thing) {
Err(e) => Some(Err(e)),
Ok(a) => {
let x = other_stuff(a);
if check_2(x) {
Some(Ok(x))
} else {
None
}
}
}
}).map(|thing_result| {
let a = try!(thing_result);
// do stuff
b
}).collect::<Result<Vec<_>, _>>();
在某些情况下,您也可以通过使用匹配来摆脱 let a = try!(thing);
。但是,在这里使用 filter_map
似乎没有帮助。
您可以自己实现这些迭代器。查看 filter
and map
是如何在标准库中实现的。
map_ok
实施:
#[derive(Clone)]
pub struct MapOkIterator<I, F> {
iter: I,
f: F,
}
impl<A, B, E, I, F> Iterator for MapOkIterator<I, F>
where
F: FnMut(A) -> B,
I: Iterator<Item = Result<A, E>>,
{
type Item = Result<B, E>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(|x| x.map(&mut self.f))
}
}
pub trait MapOkTrait {
fn map_ok<F, A, B, E>(self, func: F) -> MapOkIterator<Self, F>
where
Self: Sized + Iterator<Item = Result<A, E>>,
F: FnMut(A) -> B,
{
MapOkIterator {
iter: self,
f: func,
}
}
}
impl<I, T, E> MapOkTrait for I
where
I: Sized + Iterator<Item = Result<T, E>>,
{
}
filter_ok
几乎一样:
#[derive(Clone)]
pub struct FilterOkIterator<I, P> {
iter: I,
predicate: P,
}
impl<I, P, A, E> Iterator for FilterOkIterator<I, P>
where
P: FnMut(&A) -> bool,
I: Iterator<Item = Result<A, E>>,
{
type Item = Result<A, E>;
#[inline]
fn next(&mut self) -> Option<Result<A, E>> {
for x in self.iter.by_ref() {
match x {
Ok(xx) => if (self.predicate)(&xx) {
return Some(Ok(xx));
},
Err(_) => return Some(x),
}
}
None
}
}
pub trait FilterOkTrait {
fn filter_ok<P, A, E>(self, predicate: P) -> FilterOkIterator<Self, P>
where
Self: Sized + Iterator<Item = Result<A, E>>,
P: FnMut(&A) -> bool,
{
FilterOkIterator {
iter: self,
predicate: predicate,
}
}
}
impl<I, T, E> FilterOkTrait for I
where
I: Sized + Iterator<Item = Result<T, E>>,
{
}
您的代码可能如下所示:
["1", "2", "3", "4"]
.iter()
.map(|x| x.parse::<u16>().map(|a| a + 10))
.filter_ok(|x| x % 2 == 0)
.map_ok(|x| x + 100)
.collect::<Result<Vec<_>, std::num::ParseIntError>>()
有很多方法可以表达这个意思。
如果你只是想恐慌,使用.map(|x| x.unwrap())
。
如果你想要所有结果或一个错误,collect
变成Result<X<T>>
:
let results: Result<Vec<i32>, _> = result_i32_iter.collect();
如果您想要除错误之外的所有内容,请使用 .filter_map(|x| x.ok())
或 .flat_map(|x| x)
。
如果您想要 直到 第一个错误的所有内容,请使用 .scan((), |_, x| x.ok())
.
let results: Vec<i32> = result_i32_iter.scan((), |_, x| x.ok());
请注意,在许多情况下,这些操作可以与之前的操作结合使用。
自 Rust 1.27 起,Iterator::try_for_each
可能会引起兴趣:
An iterator method that applies a fallible function to each item in the iterator, stopping at the first error and returning that error.
This can also be thought of as the fallible form of
for_each()
or as the stateless version oftry_fold()
.