Alex
Alex

Reputation: 2402

Code within try block still panics when calling unwrap on a None value

#![feature(try_blocks)]
pub fn is_valid(code: &str) -> bool {
    let result: Result<bool, ()> = try {
        code.chars()
            .filter(|x| !x.is_whitespace())
            .map(|x| x.to_digit(10).unwrap())
            .rev()
            .enumerate()
            .map(|(i, num)| match i % 2 != 0 {
                true => {
                    let doubled = 2 * num;
                    match doubled > 9 {
                        true => doubled - 9,
                        false => doubled,
                    }
                }
                false => num,
            })
            .sum::<u32>()
            % 10
            == 0
    };
    match result {
        Ok(value) => value,
        Err(_) => false,
    }
}

On some inputs, this programme panics, and I don't understand why. The to_digit returns an Option, but it is unwrapped, and the None case should be caught by the try block. My understanding is that if the .unwrap is called on a None value, the try block will return a Result::Error, which the final match will convert into false.

I am new to Rust!

I did try x.to_digit(10).ok_or("Error").unwrap() to turn it into a Result rather than an Option, but it didn't make any difference.

Upvotes: 3

Views: 1163

Answers (1)

harmic
harmic

Reputation: 30577

My understanding is that if the .unwrap is called on a None value, the try block will return a Result::Err

Actually no: from the documentation:

Panics if the self value equals None.

A 'Panic' is very different from returning a Result::Err. Panics are intended to be used for unrecoverable errors, while returning a Result::Err is used for everyday potentially recoverable errors. See the error handling chapter in the rust book for more discussion on the differences.

As you had hinted, instead of unwrap you can use ok_or to transform the Option into a Result:

x.to_digit(10).ok_or("Error")?

Note that I put a '?' on the end, instead of the unwrap you tried. The effect of the ? is:

  • If the value is a Result::Err then it is returned from the enclosing function or try block
  • If the value is a Result::Ok then it is unwrapped.

It desugars something like this:

match x.to_digit(10).ok_or("Error") {
    Err(e) => return Err(e),
    Ok(v) => v
}

As mentioned by some in the comments, you may be better off keeping away from unstable features if you are new to rust, although I don't think that is the cause of this issue.

Upvotes: 4

Related Questions