使用结果迭代器最常用的方法是什么?

Tim*_*ean 15 iterator filterfunction rust map-function rust-result

我有这样的代码:

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<_>, _>>()
Run Code Online (Sandbox Code Playgroud)

在语义方面,我想在第一个错误后停止处理.

上面的代码有效,但感觉非常麻烦.有没有更好的办法?我查看了类似的文档filter_if_ok,但我没有找到任何东西.

我知道collect::<Result<Vec<_>, _>>,它很有效.我特意试图消除以下样板:

  • 在过滤器的关闭,我必须使用matchthing_result.我觉得这应该只是一个单行,例如.filter_if_ok(|thing| check(a)).
  • 每次我使用时map,我都要包含一个额外的声明let a = try!(thing_result);,以便处理一个可能性Err.再一次,我觉得这可以被抽象出来.map_if_ok(|thing| ...).

有没有其他方法可以用来获得这种简洁程度,或者我只是需要强硬一点?

Vee*_*rac 28

有很多方法你可以这么说.

如果你只想恐慌,请使用.map(|x| x.unwrap()).

如果您想要所有结果单个错误,请collect进入Result<X<T>>:

let results: Result<Vec<i32>, _> = result_i32_iter.collect();
Run Code Online (Sandbox Code Playgroud)

如果您想要除错误之外的所有内容,请使用.filter_map(|x| x.ok()).flat_map(|x| x).

如果您希望所有内容都出现第一个错误,请使用.scan((), |_, x| x.ok()).

let results: Vec<i32> = result_i32_iter.scan((), |_, x| x.ok());
Run Code Online (Sandbox Code Playgroud)

请注意,在许多情况下,这些操作可以与早期操作结合使用.

  • 赞成推荐 `filter_map` 方法。不知道它是_伟大的_! (2认同)
  • 上面使用的`scan`的替代方法是`take_while(Result :: is_ok).map(Result :: unwrap)` (2认同)
  • 不过,@goertzenator 如果有其他选择,最好避免使用恐慌代码。 (2认同)
  • 我正在努力编写一个会返回第一个错误结果或完整向量的减速器......但是 ``.collect()``` 太棒了! (2认同)

ark*_*kod 7

从Rust 1.27开始,Iterator::try_for_each可能会引起关注:

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

也可以将其视为的容易犯错的形式for_each()或的无状态版本try_fold()


aSp*_*pex 6

您可以自己实现这些迭代器.了解标准库中的实现方式filtermap实现方式.

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>>,
{
}
Run Code Online (Sandbox Code Playgroud)

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>>,
{
}
Run Code Online (Sandbox Code Playgroud)

您的代码可能如下所示:

["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>>()
Run Code Online (Sandbox Code Playgroud)

操场