使用结果迭代器最惯用的方法是什么? [重复]

问题描述 投票:0回答:4

我有这样的代码:

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<_>, _>>
,而且效果很好。我特别试图消除以下样板:

  • 在过滤器的关闭中,我必须在
    match
    上使用
    thing_result
    。我觉得这应该只是一句台词,例如
    .filter_if_ok(|thing| check(a))
  • 每次使用
    map
    时,我都必须包含一个额外的声明
    let a = try!(thing_result);
    ,以处理
    Err
    的可能性。再说一遍,我觉得这可以抽象为
    .map_if_ok(|thing| ...)

我可以使用另一种方法来达到这种程度的简洁性,还是我只需要坚持下去?

rust iterator map-function filterfunction rust-result
4个回答
105
投票

表达这个意思的方式有很多种。

如果您只是想惊慌,请使用

.map(|x| x.unwrap())

如果您想要所有结果单个错误,请将

collect
转换为
Result<X<T>>
,或使用别名
try_collect

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());
请注意,在许多情况下,这些操作可以与之前的操作结合使用。


23
投票
自 Rust 1.27 起,

Iterator::try_for_each

 可能会引起人们的兴趣:

一种迭代器方法,它将易出错的函数应用于迭代器中的每个项目,在第一个错误处停止并返回该错误。

这也可以被认为是

for_each()

 的易错形式或 
try_fold()
 的无状态版本。


16
投票
您可以自己实现这些迭代器。了解

filter

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

游乐场


3
投票

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

 函数以返回 
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
 似乎没有帮助。

© www.soinside.com 2019 - 2024. All rights reserved.