How to get Result's Ok value if the Result is in a Vector?

Viewed 879
#[derive(Debug)]
struct S{}

#[derive(Debug)]
struct E{}

fn test() -> Result<S, E> {
    let data_1: Result<S, E> = Ok(S{});
    let data_2: Result<S, E> = Err(E{});
    let v: Vec<Result<S, E>> = vec![data_1, data_2];

    for i in 1..2 {
        for item in &v {
            let val = item?; //error
            println!("{:?}", val);
        };
    }

    Ok(S{})
}

In the above code, I'd like to print the value of the item if the Result is Ok (otherwise return Err). But there is an error in (*item)? part due to moving a value behind shared reference:

[rustc E0507] [E] cannot move out of *item which is behind a shared reference move occurs because *item has type std::result::Result<tests::S, tests::E>, which does not implement the Copy trait

I have tried cloning the data, but it doesn't solve the problem. Besides, it doesn't sound correct to clone.

What is the correct workaround/best practice for this?

1 Answers

It's possible to format references (e.g. &S). However the ? operator needs to return the error in the Result, so you must either copy, clone or move it:

Copy

If you implement the Copy trait for your types, you can dereference &Result<S, E> (playground):

#[derive(Debug, Copy, Clone)]
struct S {}

#[derive(Debug, Copy, Clone)]
struct E {}

You can also make it work if the error type implements Copy, but not the ok type (playground).

Clone

You have to clone types, if you need to convert a reference to an owned type that isn't Copy. Example (playground):

#[derive(Debug, Clone)]
struct S {}

#[derive(Debug, Clone)]
struct E {}

let val = item.clone()?;

You can change this to only clone in case of an error (playground):

#[derive(Debug)]
struct S {}

#[derive(Debug, Clone)]
struct E {}

let val = item.as_ref().map_err(Clone::clone)?;

Move

If you don't need the vector after iterating over it, you can move it:

for item in v {  // this calls `IntoIterator::into_iter(v)` implicitly
    let val = item?;
    println!("{:?}", val);
}

Or, you can convert the Vec<Result<S, E>> to a Result<Vec<S>, E> first:

// collects the `Ok` values, or returns the first error it encounters
let v: Vec<S> = v.into_iter().collect::<Result<_, _>>()?;

for item in &v {
    println!("{:?}", item);
}
Related