Chaining failible maps

Heya peeps,

While working on today's advent of code, I found myself with a question regarding the chaining of map calls when each mapped function returns a Result.

I have the following code:

    pub fn sum_of_badge_priorities(&self) -> Result<u32, &'static str> {
        self.0
            .iter()
            .map(|group| group.find_badge().and_then(Rucksack::priority))
            .sum()
    }

For context, I have a vector of groups, each group contains a badge, each badge can have a priority calculated on it. The goal of this function is to return the sum of the priorities of each badge in the groups.

Of course, the Group::find_badge and Rucksack::priority functions can fail, e.g. because there is no badge or because the badge is invalid and cannot be assigned a priority. In case of failure, nothing should be calculated and the whole function should return the appropriate Err.

This code works, but out of curiosity I tried to separate the complicated map into two successive maps, like so:

    pub fn sum_of_badge_priorities(&self) -> Result<u32, &'static str> {
        self.0
            .iter()
            .map(Group::find_badge)
            .map(Rucksack::priority)
            .sum()
    }

Of course, this code fails, since I give a Result<Badge> to Rucksack::priority. I could make the code compile by transforming the first map into a flat_map, but the behavior is subtly different: in one case of error, the group will be silently discarded, and sum will work on a empty iterator, returning Ok(0).

Basically, I'm wondering if there is a sort of bind operator for iterators, some kind of super and_then that will behave like the first code snippet. I didn't find any try_map method, but some discussion explaining while this function would need to eagerly evaluate as the reason for its non-existence. I also found the very helpful Iterating over Results page, but it doesn't address the case of chaining multiple maps.

I guess this is also a question about style and idiomatic Rust: in this kind of scenario, what would be the most idiomatic? Having a "big" map that does all the computation or chaining different maps? (Of course there's simply the possibility of extracting that function composition into its own function and map over it, but this is just for fun).

Cheers!

You could do something like .map(|badge_result| badge_result.and_then(Rucksack::priority)) for the second map.

    pub fn sum_of_badge_priorities(&self) -> Result<u32, &'static str> {
        self.0
            .iter()
            .map(Group::find_badge)
            .map(|badge_result| badge_result.and_then(Rucksack::priority))
            .sum()
    }

About making this more concise, there is a few operators for Result-valued iterators in the itertools crate, but the closest I could find, map_ok isn’t quite what you need here.


On second thought… given that this is in a closure, you could use the ? operator to get somewhat of a nicer syntax as in .map(|b| Rucksack::priority(b?)). The ? operator would of course also be usable in the original code where the map are combined.