1

I am reading Rust by Example book. In this example removing return in Err(e) => return Err(e) causes an error: expected `i32`, found enum `Result`` . Why is that?

What is the difference between Err(e) => return Err(e) and Err(e) => Err(e)?

Here is the code from the example:

use std::num::ParseIntError;

fn main() -> Result<(), ParseIntError> {
    let number_str = "10";
    let number = match number_str.parse::<i32>() {
        Ok(number)  => number,
        Err(e) => return Err(e),
    };
    println!("{}", number);
    Ok(())
}
Peter
  • 656
  • 1
  • 6
  • 14
  • 3
    Note that the result of the `match` is being bound to `number`. What could the type of `number` be? In your version, the result of the match could be either of type `i32` or `Result`, and the type of `number` can only be one or the other, chosen at compile time. – Nate Eldredge Mar 20 '22 at 03:01

2 Answers2

5
    let number = match number_str.parse::<i32>() {
        Ok(number)  => number,
        Err(e) => return Err(e),
    };

This says if number_str.parse::<i32>() returns an Ok to set number to number, an i32. If it returns an Err to immediately return Err(e), which is a Result, from main, number is not set.

That's fine because number can only be an i32 and main does return a Result.

    let number = match number_str.parse::<i32>() {
        Ok(number)  => number,
        Err(e) => Err(e),
    };

This says if number_str.parse::<i32>() returns an Ok to set number to number, an i32, same as before. If it returns an Err to set number to Err(e), which is a Result.

number cannot be both an i32 and a Result, so you get a compiler error.

Schwern
  • 153,029
  • 25
  • 195
  • 336
3

All arms of a match have to have "compatible" types. This either mean they all have the same type:

fn is_some<T>(opt: &Option<T>) -> bool {
  match opt {
    // both arms have type bool
    Some(_) => true,
    None => false,
  }
}

or some of the branches need to direct control flow away from the match in some way.

The main rule to keep in your head is that every variable in Rust needs a single type (ignoring variance w.r.t. lifetimes). In your case, if you didn't have the return keyword, what would be the type of number?

If the parse succeeds, number would be an i32. If it fails, it would be an Err(ParseIntError). This isn't allowed, so you get a compile error.

However, if you return Err(e), number will always be an i32 (since return prevents the rest of the function running, so number doesn't need a type).

A similar thing applies to other control-flow keywords (e.g. continue, break, ...), as well as things that are guaranteed to not return, like panic!() (look up the Never/! type for more info)

cameron1024
  • 9,083
  • 2
  • 16
  • 36