Map conditonally

use std::error::Error;
use anyhow::{anyhow, Result};

fn main() -> Result<(), Box<dyn Error>> {
    let v = vec![0, 1, 2, 3];
    let c = v.into_iter().map(evens_doubled).collect::<Result<Vec<_>>>()?;
    dbg!(c);
    Ok(())
}

fn evens_doubled(n: u32) -> Result<Option<u32>> {
    if n == 0 || n == 2 {
        Ok(Some(n * 2))
    } else if n == 1 || n == 3 {
        Ok(None)
    } else {
        Err(anyhow!("something went wrong"))
    }
}

Playground

This currently outputs [Some(0), None, Some(4), None].

I'd like to have [0, 4].

    let c = v.into_iter()
        .map(evens_doubled)
        .filter_map(Result::transpose)
        .collect::<Result<Vec<_>>>()?;

Playground

Edit: You could also combine the map and filter_map like this, which might be a bit clearer:

    let c = v.into_iter()
        .filter_map(|x| evens_doubled(x).transpose())
        .collect::<Result<Vec<_>>>()?;
2 Likes

Ah, that's good. I had tried using filter_map() and adjusting evens_doubled() accordingly but then I couldn't use ? inside evens_doubled() anymore. Transposing the Result into the Option afterwards solves that problem.