How to Tackle Error in Rust Programming

Reading Time: 3 minutes

An Error is basically an unexpected behaviour that may lead a program to produce undesired output or terminate abruptly. Error handling is the process of handling the possibility of failure. In Rust, errors can be classified into two major categories.

  • Recoverable
  • UnRecoverable

Recoverable Error

Recoverable errors are those that do not cause the program to terminate abruptly. Such as a file not found error . It’s reasonable to report the problem to the user and retry the operation. Instead of panicking, you emit a Option <T> or Result<T,E>. In these cases, you have a choice between a valid value or an invalid value.

UnRecoverable Error

Unrecoverable errors are those that cause the program to terminate abruptly. A program cannot revert to its normal state if an unrecoverable errors occurs. In these cases, Rust has the panic! macro. The panic macro causes the program to exit abruptly. The panic! prints the failure message. The panic! macro unwinds cleans up the stack and then quit.

Ways to handle the Error

In Rust there are multiple ways in errors can Handled. Some are :

  • panic! marco
  • Result <T,E> enum
  • Option <T> enum
  • Unwrap method
  • Except method
  • ? operator

panic! marco

panic! macro allows a program to terminate immediately and provide feedback to the caller of the program. We should only use panic in a condition if our code may end up in a bad state.

Example :

fn main() {
    panic!("panic condition !! ");
}

Output :

Compiling playground v0.0.1 (/playground)
    Finished dev [unoptimized + debuginfo] target(s) in 0.99s
     Running `target/debug/playground`
thread 'main' panicked at 'panic condition !! ', src/main.rs:2:5

Result <T,E> Enum

Rust does not have exceptions. It uses the Result enum, which is used to represent either success (Ok) or failure (Err).

Example :

use std::fs::File;

fn main() {
    let file = File::open("test.txt");
   match file {
      Ok(file)=> println!("file found {:?}",file),
      Err(error)=> println!("file not found error {:?} ",error)
      }
}

Output :

Compiling playground v0.0.1 (/playground)
    Finished dev [unoptimized + debuginfo] target(s) in 2.48s
     Running `target/debug/playground`
file not found error Os { code: 2, kind: NotFound, message: "No such file or directory" }

Option <T> Enum

An optional value can have either Some value or no value/ None.

Example :

fn main() {
    let res = division(0,10);
    match res {
        Some(rem) => println!("Remainder is {}",rem),
        None => println!("Error : Divide by Zero")
    }
}

fn division(divisor:i32,dividend:i32) -> Option<i32> {
    if divisor == 0 {
        None
    } else {
        Some(dividend / divisor)
    }
}

Output :

   Compiling playground v0.0.1 (/playground)
    Finished dev [unoptimized + debuginfo] target(s) in 0.95s
     Running `target/debug/playground`
Error : Divide by Zero

Unwrap Method

The Unwrap method returns a panic! macro when it receives a None.

Example :

fn main() {
    let res = division(0,10);
    println!("{}",res.unwrap());
}

fn division(divisor:i32,dividend:i32) -> Option<i32> {
    if divisor == 0 {
        None
    } else {
        Some(dividend / divisor)
    }
}

Output :

Compiling playground v0.0.1 (/playground)
    Finished dev [unoptimized + debuginfo] target(s) in 0.96s
     Running `target/debug/playground`
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', src/main.rs:3:23
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Except Method

The Except method is identical to Unwrap method but it allows you to set an error message.

Example :

fn main() {
    let res = division(0,10);
    println!("{}",res.expect("Error : Divide by Zero"));
}

fn division(divisor:i32,dividend:i32) -> Option<i32> {
    if divisor == 0 {
        None
    } else {
        Some(dividend / divisor)
    }
}

Output :

Compiling playground v0.0.1 (/playground)
    Finished dev [unoptimized + debuginfo] target(s) in 0.99s
     Running `target/debug/playground`
thread 'main' panicked at 'Error : Divide by Zero', src/main.rs:3:23
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

? Operator

We can unpack Option enum by using match statements, but it’s often easier to use the ? operator.

Example :

#[derive(Debug)]
    enum MathError {
        DivisionByZero,
    }
fn main() {
    let res = check_division(0,10);
    println!("{:?}",res);
}

fn check_division(divisor: i32, dividend: i32) -> Result<i32,MathError>  {
        let rem = division(divisor, dividend)?;
        return Ok(rem)
    }

fn division(divisor:i32,dividend:i32) -> Result<i32,MathError> {
    if divisor == 0 {
        Err(MathError::DivisionByZero)
    } else {
        Ok(dividend/divisor)
    }
}

Output :

 Compiling playground v0.0.1 (/playground)
    Finished dev [unoptimized + debuginfo] target(s) in 0.97s
     Running `target/debug/playground`
Err(DivisionByZero)

So I hope you got some idea how Rust Tackle Errors. Thanks for Reading !!

If you want to read more content like this?  Subscribe to Rust Times Newsletter and receive insights and latest updates, bi-weekly, straight into your inbox. Subscribe to Rust Times Newsletter: https://bit.ly/2Vdlld7.


Knoldus-blog-footer-image

Leave a Reply