2

I have a function which returns a Result<u32, SomeError>. I want to collect four values from this function, and collect them to sum them up. How can you propagate the error inside the map operator? I want to stop on the first error, and propagate the error up to the function which called it

Works

(0..4) .map(|_| self.consume().unwrap()) .collect::<Vec<u32>>() .iter() .sum::<u32>(); 

Does not work

(0..4) .map(|_| self.consume()?) .collect::<Vec<u32>>() .iter() .sum::<u32>(); 

The compiler error is

.map(|_| self.consume()?) ----^^^^^^^^^^^^^^^---------------------- | | | cannot use the `?` operator in a closure that returns `u32` this function should return `Result` or `Option` to accept `?` 
1
  • 1
    When you encounter an error, what do you want to do? Skip it? Collect into vec of Results? Stop on the first error? Collect into a Result<Vec> and fail completely if there was an error? Commented Jun 15, 2022 at 16:15

3 Answers 3

3

map() is just an iterator adapater. It cannot propagate the error itself, since it doesn't run the code that can produce it. The executor of the code here is sum() (actually, collect(), but it is redundant here) and thus it should deal with the error (or a replacement).

In this case, you can exploit the fact that Result implements Sum, allowing you to sum into Result<T, E>:

(0..4).map(|_| self.consume()).sum::<Result<u32, _>>()?; 

collect() also does the same, so you can collect into Result<Vec<T>, E> for example.

Sign up to request clarification or add additional context in comments.

4 Comments

For both myself and the OP, does this occur as you go, or are all of the map calls done before the sum is attempted? That was one of the OP's requirements, that it stops on the first error.
@KevinAnderson As I said, map() does nothing but produces an iterator. It is lazy (otherwise, it would have to keep the results in a Vec). The real work is done in sum(), and it processes the element until the first error, by that time it discards the result and returns the error.
@ChayimFriedman Thank you very much! Is it possible to do something similar also if consume returns Result<char, _>, and then apply .to_digit(x) (.unwrap() on each of these chars? (this self.consume().unwrap().to_digit(16).unwrap())
@jevoso5871 If they return the same error type, it's as easy as self.consume()?.to_digit(). If not, you may need to be more explicit and maybe specify the error type. But nonetheless, inside the closure you can use the normal ? operator and all error handling facilities - you just need to return Result from it.
2

There's actually a Sum instance for Result<T, E> that fits this usecase perfectly:

(0..4) .map(|_| self.consume()) .sum::<Result<u32, _>>()?; 

Comments

2

Here you go:

(0..4) .map(|_| self.consume()) .collect::<Result<Vec<u32>, _>>()? .iter() .sum::<u32>(); 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.