How to unpack the sequence of results <(A, B), E> to (Vec <a>, Vec <b>) and stop the first error?

I would like to do something like this:

enum MyEnum {
    Foo(Vec<Stuff>),
    // ...
}

impl MyEnum {
    fn do_sth(&self) -> Result<Bar, E> {
        match *self {
            MyEnum::Foo(ref vec) => {
                let (a, b): (Vec<A>, Vec<B>) = vec
                    .iter()
                    .map(|thing| thing.make_a_tuple(arg)) // returns Result<(A, B), E>
                    .collect::<Result<_, _>>()? // stop on first error
                    .unzip();

                // use a and b
            }

            // other cases
        }

        // ...
    }
}

This does not compile with error: the type of this value must be known in this context. Through the trial version and the error, I got it to compile as follows

    fn do_sth(&self) -> Result<Bar, E> {
        match *self {
            MyEnum::Foo(ref vec) => {
                let (a, b): (Vec<A>, Vec<B>) = vec
                    .iter()
                    .map(|thing| thing.make_a_tuple(arg))
                    .collect::<Result<Vec<_>, _>>()?
                    .into_iter()
                    .unzip();

                // use a and b
            }

            // other cases
        }
    }

However, I would like to avoid unnecessary highlighting Vec<(A, B)>.

Can this be done without intermediate distributions? I'm sure I could do it myself with a loop, but I would rather learn Rusty.

+4
source share
1 answer

, Result s. , () , ResultShunt. Sum Product Result s. itertools, :

fn main() {
    let iter_source: Vec<Result<(i32, i32), bool>> =
        vec![Ok((1, 2)), Err(false), Ok((3, 4))];

    let z = ResultShunt::process(iter_source.into_iter(), |iter| {
        iter.unzip::<_, _, Vec<_>, Vec<_>>()
    });

    println!("{:?}", z);
}

// ----- Begin copy-pasta -----

/// An iterator adapter that produces output as long as the underlying
/// iterator produces `Result::Ok` values.
///
/// If an error is encountered, the iterator stops and the error is
/// stored. The error may be recovered later via `reconstruct`.
struct ResultShunt<I, E> {
    iter: I,
    error: Option<E>,
}

impl<I, T, E> ResultShunt<I, E>
    where I: Iterator<Item = Result<T, E>>
{
    /// Process the given iterator as if it yielded a `T` instead of a
    /// `Result<T, _>`. Any errors will stop the inner iterator and
    /// the overall result will be an error.
    pub fn process<F, U>(iter: I, mut f: F) -> Result<U, E>
        where F: FnMut(&mut Self) -> U
    {
        let mut shunt = ResultShunt::new(iter);
        let value = f(shunt.by_ref());
        shunt.reconstruct(value)
    }

    fn new(iter: I) -> Self {
        ResultShunt {
            iter: iter,
            error: None,
        }
    }

    /// Consume the adapter and rebuild a `Result` value. This should
    /// *always* be called, otherwise any potential error would be
    /// lost.
    fn reconstruct<U>(self, val: U) -> Result<U, E> {
        match self.error {
            None => Ok(val),
            Some(e) => Err(e),
        }
    }
}

impl<I, T, E> Iterator for ResultShunt<I, E>
    where I: Iterator<Item = Result<T, E>>
{
    type Item = T;

    fn next(&mut self) -> Option<Self::Item> {
        match self.iter.next() {
            Some(Ok(v)) => Some(v),
            Some(Err(e)) => {
                self.error = Some(e);
                None
            }
            None => None,
        }
    }
}
+3

Source: https://habr.com/ru/post/1679255/


All Articles