Errors in functions

Now let us take a look at how Rust does error handling. First off, contrary to most other languages, recoverable errors in Rust are explicit. As with null handling, the standard library contains an enum for this exact purpose:

pub enum Result<T, E> {
    Ok(T),
    Err(E),
}

Code using a function returning this enum have to explicitly check for success to access the return value. Even when disregarding the value, the compiler will issue a warning if you do not check a result. In an error case, information about the error will be returned. This can be used creatively, for example as done in the binary_search function of slices. On success, it returns the index of the found element, on failure, it returns the index where you could insert the element such that the slice stays sorted. Handling errors using the Result enum gives you the choice of ignoring the error and doing nothing, handling it, or escalating it to the caller. Of course, the latter only works if your function also returns a Result with the appropriate type. You can also use the unwrap function to convert it into a non-recoverable error, but with the same caveats found in null handling.

While always explicitly handling errors is very verbose, even if you just want to escalate it, Rust actually provides some support in the language itself, as opposed to the standard library, by using the ? operator. Using this operator unwraps the return value in the success case, or returns the error to the calling function in an error case. It also wraps your error in a Box if needed, so you can use it to convert a concrete error to a boxed dynamic type. This enables you to escalate different errors from called functions.

use std::fs::File;
use std::io::Read;

fn read_number_from_file() -> Result<usize, Box<dyn std::error::Error>> {
  // Can return a std::io::Error
  let mut file = File::open("fun.txt")?;
  
  let mut buffer = String::new();
  // Can return a std::io::Error
  file.read_to_string(&mut buffer)?;
  // Can return a std::str::FromStr::Err
  let number = buffer.parse::<usize>()?;
  
  Ok(number)
}