Passing error out of a map for early return idiomatically

I'm having trouble figuring out how to pass an error through a closure idiomatically.

I'm trying to have this piece of code print Ok([0, 1, 0, 2, 0, 3]).

fn increment(n: u8) -> Result<Vec<u8>, String> {
    Ok(vec![0,n])
}

fn build_big_vec() -> Result<Vec<u8>, String> {
    let nums = vec![1u8, 2, 3];
    let bigvec: Vec<u8> = nums
        .iter()
        .map(|&n| increment(n)?)
        .flatten()
        .collect();
    Ok(bigvec)
}

fn main() {
    let bigvec = build_big_vec();
    println!("bigvec: {:?}", bigvec);
}

If I change .map(|&n| increment(n)?) to .map(|&n| increment(n).unwrap()), it works perfectly. I guess I could use a for loop instead of a map, but was wondering if there was a better alternative.

One option:

fn build_big_vec() -> Result<Vec<u8>, String> {
    let nums = vec![1u8, 2, 3];
    let bigvec: Vec<u8> = nums
        .into_iter()
        .map(|n| increment(n))
        .collect::<Result<Vec<_>, _>>()?
        .into_iter() 
        .flatten()
        .collect();
    Ok(bigvec)
}

This collects all the results of increment and bails early if there is an error. Then we use ? to get the Vec<Vec<u8>> and continue on to flattening.

Here's some info about dealing with iterators of results: https://doc.rust-lang.org/rust-by-example/error/iter_result.html.

Heres the playground of the example: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=811af2810ca47232f0a188b6660592fd.

1 Like

I would probably do:

fn build_big_vec() -> Result<Vec<u8>, String> {
    let nums = vec![1u8, 2, 3];
    let mut result = Vec::new(); // consider Vec::with_capacity(size) if known
    for intermediate in nums.iter().map(|&n| increment(n)) {
        result.extend(intermediate?);
    }
    Ok(result)
}

If you really hate for loops for some reason, you could do:

fn build_big_vec() -> Result<Vec<u8>, String> {
    let nums = vec![1u8, 2, 3];
    Ok(nums
        .iter()
        .map(|&n| increment(n))
        .collect::<Result<Vec<_>, _>>()?
        .into_iter()
        .flatten()
        .collect())
}

(But I don't recommend it due to the double-collect and not being any more clear IMO.)

3 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.