0

I need to chunk my vector Vec<Result<SomeStruct>> into pieces, iterate over each piece, do some work, and return an error if Result<SomeStruct> contains it.

enum SomeStruct {}

#[derive(Debug)]
struct SomeError {}

type Result<T> = std::result::Result<T, SomeError>;

fn foo(v: Vec<Result<SomeStruct>>) -> Result<()> {
    for chunk in v.chunks(42) {
        for value in chunk {
            let value = value?;
            //do smth with value
        }
    }
    Ok(())
}

fn main() {
    let mut values = Vec::new();
    foo(values).unwrap();
}

playground

However, I get the error

error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try`
  --> src/main.rs:11:25
   |
11 |             let value = value?;
   |                         ^^^^^^ the `?` operator cannot be applied to type `&std::result::Result<SomeStruct, SomeError>`
   |
   = help: the trait `std::ops::Try` is not implemented for `&std::result::Result<SomeStruct, SomeError>`
   = note: required by `std::ops::Try::into_result`

It seems ? expects the value itself instead of a shared reference to the value, but I can't dereference a shared reference and don't know how to get this value itself because chunk here is &[Result<SomeStruct>], and value is &Result<SomeStruct>

How can I fix it?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
NikBond
  • 743
  • 1
  • 5
  • 20
  • 1
    Can you give an example of what you're doing that requires chunking? You may be able to just use a straight-up `for` loop over `v` instead, pausing every 42 elements. – PitaJ Nov 18 '19 at 22:55

2 Answers2

3

General case

Your function signature requires that you return an owned SomeError, but all you have is a reference to one. Unless you have some way of converting a &SomeError into a SomeError, this problem is unsolvable.

The most applicable solution is to change your error to be clonable (or copyable, if appropriate) and then convert your &Result<T, E> into a Result<&T, E> via Result::as_ref and Result::map_err:

#[derive(Debug, Clone)]
struct SomeError {}
for value in chunk {
    let value = value.as_ref().map_err(Clone::clone)?;
    // do something with `value`
}

Also possible, but far less common, would be to return a reference to the error. This requires that the error lives longer than the call to the function:

type Result<T, E = SomeError> = std::result::Result<T, E>;

fn foo(v: &[Result<SomeStruct>]) -> Result<(), &SomeError> {
    for chunk in v.chunks(42) {
        for value in chunk {
            let value = value.as_ref()?;
            // do something with `value`
        }
    }
    Ok(())
}

fn main() {
    let values = Vec::new();
    foo(&values).unwrap();
}

See also:

Specific case

Since you take ownership of the Vec and don't make use of the fact that the inner iterator is a slice, you don't need to have a &Result to start with. Instead, convert the Vec into an iterator and take chunk-length pieces of it:

fn foo(v: Vec<Result<SomeStruct>>) -> Result<()> {
    let mut v = v.into_iter().peekable();

    while v.peek().is_some() {
        for value in v.by_ref().take(42) {
            let _value = value?;
            // do something with `value`
        }
    }

    Ok(())
}

You can also use Itertools::chunks:

use itertools::Itertools; // 0.8.1

fn foo(v: Vec<Result<SomeStruct>>) -> Result<()> {
    for chunk in &v.into_iter().chunks(42) {
        for value in chunk {
            let value = value?;
            // do something with `value`
        }
    }
    Ok(())
}

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
1

Understanding the Error

Here is the signature of Vec::chunks:

pub fn chunks(&self, chunk_size: usize) -> Chunks<T>

Notice &self, because the Chunks<T> returned has an iterator of type Item = &'a [T]. When you iterate over this slice (each chunks is a slice) you get a reference to a T. Hence the error the compiler gives:

the trait `std::ops::Try` is not implemented for `&std::result::Result<SomeStruct, SomeError>`

Basically this is saying Try is not implemented for &Result<SomeStruct>.

Solution

I don't think it is not possible to take ownership of a reference of a slice (without copying).

Copy to a Vec:

You can use to_vec on the slice (chunk) provided that SomeStruct & SomeError implement Clone.

Use SomeStruct as a reference:

Since your code returns Ok(()), you might be able to use a &SomeStruct in //do smth with value. In fact this is a common, and very useful pattern in Rust:

fn do_something_with_value(v: &SomeStruct) {
    unimplemented!();
}
muhuk
  • 15,777
  • 9
  • 59
  • 98
  • You can also write `let value = value.as_ref().map_err(SomeError::clone)?` (adding a `derive(Clone)` for the `SomeError`) and clone only in the case of error. – rodrigo Nov 18 '19 at 22:16
  • @Shepmaster you are right, if `to_vec` is used `SomeError` also needs to be cloneable. Thanks. I updated the answer. – muhuk Nov 19 '19 at 07:15