3
#[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?

Pahlevi Fikri Auliya
  • 4,157
  • 8
  • 35
  • 71
  • 1
    Does this answer your question? [How do I stop iteration and return an error when Iterator::map returns a Result::Err?](https://stackoverflow.com/questions/26368288/how-do-i-stop-iteration-and-return-an-error-when-iteratormap-returns-a-result) – E_net4 May 23 '20 at 17:25
  • I think it could be a workaround. Out of curiosity, how if we would like to do it without relying on iterator? (i.e. still use for loop) – Pahlevi Fikri Auliya May 23 '20 at 17:31
  • Is simply removing the reference (`for item in v`) a solution for you? If not, can you explain what you want to return? You can't return a reference to a value created inside a function. – trent May 23 '20 at 17:37
  • 1
    For loops still use iterators internally. That is traversing an iterator where `Item = &Result`. IIRC you can turn this into a `Result<&S, &E>` with a method (`as_ref`). How about just pattern matching over each item? `if let Ok(val) = item { ... }` – E_net4 May 23 '20 at 17:39
  • @trentcl it does in this case, but it doesn't in my real case (original question updated). I can't do `for item in v` because that looping is actually nested inside another loop. Hence, doing so would result in `use of moved value: "v". value moved here, in previous iteration of loop` error – Pahlevi Fikri Auliya May 23 '20 at 17:46
  • I want to return a value derived from the Ok data – Pahlevi Fikri Auliya May 23 '20 at 17:49
  • 1
    If in your real use case you have a function that takes `v` by reference and that's why you can't iterate over it by value, you could have it return `Result` (where the `S` is created inside the function and the `&E` a reference to the argument). If `v` is created inside the function but you want to iterate over it multiple times, I don't know if it makes sense to avoid cloning the `E`. – trent May 23 '20 at 17:51
  • 1
    Maybe what you want is to pre-process the `Vec>` and return `Err` if any of them are `Err`, and then proceed to the rest of your algorithm with a `Vec`? That sounds reasonable (although it calls into question why you have a `Vec` of `Result`s in the first place). – trent May 23 '20 at 17:53
  • `match item { Ok(val) => println!("{:?}", val), Err(err) => return Err(err) }` I tried exactly as you two described, yes the return would be `Result` (which doesn't match the signature (`Result`) Newbie question, how to convert `&E` to `E`? `return Err(err.clone())` doesn't work, the type is still `&E` – Pahlevi Fikri Auliya May 23 '20 at 17:55
  • [Here's what I wrote that works.](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=facb5ded7dd37876a012a4786aad226a) (Basically just a rehash of Aloso's answer) – trent May 23 '20 at 18:10
  • I see, for the sake of other answer-seekers reading this: `struct E` needs to `#[derive(Clone)]`. Thx all, I learned a lot from this discussion – Pahlevi Fikri Auliya May 23 '20 at 18:14

1 Answers1

2

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);
}
Aloso
  • 5,123
  • 4
  • 24
  • 41
  • @pahlevi-fikri-auliya thanks for accepting my answer! May I ask what method you used? – Aloso May 23 '20 at 18:14
  • They all work in the simplified code I put here. Still trying in my real code, like the answer with Clone turns out requiring adding Clone in many other parts. I am curious about the last option, why does converting `Vec>` to `Result, E>` help? – Pahlevi Fikri Auliya May 23 '20 at 18:21
  • The last example works, because it doesn't return an error in the `for` loop with `?`. It only uses `?` on the `Result, E>`, which is not behind a reference. – Aloso May 23 '20 at 19:09