The try! macro and the ? operator

When, during a computation, several function calls return a Result type value, the handling of the error propagation through pattern matching can become quite tedious-each returned Result type value requires a match to differentiate between an Ok value and an Err value.

We had exactly such a situation in the input_number.rs program, where we read an input from the terminal and tried to parse it into an unsigned integer, both operations can fail.

The try! macro was specifically made in order to simplify such code and make error-handling more elegant and readable.

The try! macro can only be used from inside a function that returns a Result type value, so not from within the main() function. Also, it can only be used on functions that return a Result type value. It returns the Ok value, and then continues with the function's code. But in case of an error the Err value is returned, returning immediately from the enclosing function. If we apply this to input_number.rs, we get much more readable code, as follows:

// see code in Chapter 5/code/try_input_number.rs 
use std::io; 
use std::error; 
 
fn main() { 
    println!("Give a positive secret number: "); 
    match input_num() { 
        Ok(v) => println!("Input value is: {}", v), 
  Err(e) => println!("Error - Please input an integer number!: {}", e) 
    } 
} 
 
fn input_num() -> Result<u32, Box<error::Error>> { 
    let mut input = String::new(); 
    try!(io::stdin().read_line(&mut input)); 
    Ok(try!(input.trim().parse())) 
} 

When there is no input, the program prints out the following error:

    Error - Please input an integer number!: cannot parse integer from empty string  

Then, the program exits normally. When a non-digit input is given, the program prints out to give a positive secret number, as follows:

    Error - Please input an integer number!: invalid digit found in string  

Then, it exits normally.

The dangerous operations are wrapped inside the function input_num. We can use the try! macro on these operations because the input_num function returns a Result type value.

Our code becomes even shorter when we use the error propagation operator ?, which works the same way as the try! macro:

  fn input_num() -> Result<u32, Box<error::Error>> { 
       let mut input = String::new(); 
    io::stdin().read_line(&mut input)?; 
    Ok(input.trim().parse()?) 
} 

The Box<error::Error>> is a so called boxed pointer, a reference to an Error instance from the std::error module. We talk more about the Box pointer in Chapter 7, Ensuring Memory Safety and Pointers. We need to use a Box pointer here to let the compiler know that we don't know the size of the error instance.

If all you need from errors is their string content, you could simplify error-handling by using the simple error crate (https://crates.io/crates/simple-error).