Search code examples
error-handlingrustrust-result

What's the idiomatic Rust way to wrap a non-error function with Result?


I have a function that parses a str to a number and returns it or an error

fn parse_str<T: FromStr>(text: &str) -> Result<T, SaleError> {
    match text.parse::<T>() {
        Ok(value) => Ok(value),
        _ => Err(SaleError::new(6010, 1999)),
    }
}

SaleError is my custom error struct.

After parsing, I want to do some other checking in the result value, for example, check if the number is positive with is_sign_positive(), if it's not, I want to issue an Err(SaleError).

Since is_sign_positive() returns only a bool, what I've done was create this function:

fn is_positive(number: f64) -> Result<f64, SaleError> {
    match number.is_sign_positive() {
        true => Ok(number),
        false => Err(SaleError::new(6010, 1999)),
    }
}

Now I can use it like that:

let value1 = try!(parse_str::<f64>("123").and_then(is_positive)
                  .or(Err(SaleError::new(6010, 1465))));
let value2 = try!(parse_str::<f64>("321").and_then(is_positive)
                  .or(Err(SaleError::new(6010, 5000))));

This works great, but note that I want a specific SaleError instance for value1 and value2, so I've used the or() function.

Now, since I will always want a specific error instead of the one is_positive returns to me, is it possible to wrap is_sign_positive() so I can use it without the need to create the function is_positive.

Something like that:

let value1 = try!(parse_str::<f64>("123").check_if(|n| n.is_sign_positive())
                  .or(Err(SaleError::new(6010, 1465))));

Solution

  • I would probably just use an if:

    "123"
        .parse::<f64>()
        .map_err(|_| SaleError(6010, 1999))
        .and_then(|n| {
            if n.is_sign_positive() {
                Ok(n)
            } else {
                Err(SaleError(6010, 1465))
            }
        });
    

    If you really want it though, you can make it:

    struct SaleError(i32, i32);
    
    fn main() {
        "123"
            .parse::<f64>()
            .map_err(|_| SaleError(6010, 1999))
            .wonky(SaleError(6010, 1465), |n| n.is_sign_positive());
    }
    
    trait Wonky<T, E> {
        fn wonky<F>(self, error: E, f: F) -> Result<T, E>
            where F: FnOnce(&T) -> bool;
    }
    
    impl<T, E> Wonky<T, E> for Result<T, E> {
        fn wonky<F>(self, error: E, f: F) -> Result<T, E>
            where F: FnOnce(&T) -> bool
        {
            match self {
                Ok(n) => if f(&n) { Ok(n) } else { Err(error) },
                Err(e) => Err(e),
            }
        }
    }