Search code examples
rusterror-handlingfunctional-programming

How do I chain functions returning results in Rust?


In the following code I would like to get the 2nd string in a vector args and then parse it into an i32. This code will not complie, however, because i can not call parse() on the Option value returned by nth().

use std::env;

fn main() {
    let args: Vec<String> = env::args().collect();
    let a = args.iter().nth(1).parse::<i32>();
}

I know i could just use expect() to unwrap the value, before trying to parse it, however I do not want my code to panic. I want a to be a Result value that is an Err if either nth() or parse() fails, and otherwise is a Ok(Int). Is there a way to accomplish this in rust? Thanks.


Solution

  • It is quite easy if you look in the documentation for either Option or Result. The function you are thinking of is likely and_then which allows you to then provide a closure which can change the Ok type and value if filled, but otherwise leaves it unchanged when encountering an error. However, you need to do though is decide on a common error type to propagate. Since the Option<&String> needs to be turned to an error on a None value we have to choose a type to use.

    Here I provide a brief example with a custom error type. I decided to use .get instead of .iter().nth(1) since it does the same thing and we might as well take advantage of the Vec since you have gone to the work of creating it.

    use std::num::ParseIntError;
    
    enum ArgParseError {
        NotFound(usize),
        InvalidArg(ParseIntError),
    }
    
    
    let args: Vec<String> = env::args().collect();
    let a: Result<i32, ArgParseError> = args
        .get(1)  // Option<&String>
        .ok_or_else(|| ArgParseError::NotFound(1))  // Result<&String, ArgParseError>
        .and_then(|x: &String| {
            x.parse::<i32>() // Result<i32, ParseIntError>
                .map_err(|e| ArgParseError::InvalidArg(e)) // Result<i32, ArgParseError>
        });