Rust – Error Handling

  • Post author:
  • Post category:Rust
  • Post comments:1 Comment
Rust - Error Handling

Rust Error Handling can be classified into two major categories as shown in the table below.

Sr.NoName & DescriptionUsage
1RecoverableErrors which can be handledResult enum
2UnRecoverableErrors which cannot be handledpanic macro

A recoverable error is an error that can be corrected. A program can retry the failed operation or specify an alternate course of action when it encounters a recoverable error. Recoverable errors do not cause a program to fail abruptly. An example of a recoverable error is the File Not Found error.

Unrecoverable errors cause a program to fail abruptly. A program cannot revert to its normal state if an unrecoverable error occurs. It cannot retry the failed operation or undo the error. An example of an unrecoverable error is trying to access a location beyond the end of an array.

Unlike other programming languages, Rust does not have exceptions. It returns an enum Result<T, E> for recoverable errors, while it calls the panic macro if the program encounters an unrecoverable error. The panic macro causes the program to exit abruptly.

Panic Macro and Unrecoverable Rust Error Handling

panic! the macro allows a program to terminate immediately and provide feedback to the caller of the program. It should be used when a program reaches an unrecoverable state.

fn main() {
   panic!("Hello");
   println!("End of main"); //unreachable statement
}

In the above example, the program will terminate immediately when it encounters panic! macro.

Output

thread 'main' panicked at 'Hello', main.rs:3

Illustration: panic! macro

fn main() {
   let a = [10,20,30];
   a[10]; //invokes a panic since index 10 cannot be reached
}

Output is as shown below −

warning: this expression will panic at run-time
--> main.rs:4:4
  |
4 | a[10];
  | ^^^^^ index out of bounds: the len is 3 but the index is 10

$main
thread 'main' panicked at 'index out of bounds: the len 
is 3 but the index is 10', main.rs:4
note: Run with `RUST_BACKTRACE=1` for a backtrace.

A program can invoke panic! macro if business rules are violated as shown in the example below −

fn main() {
   let no = 13; 
   //try with odd and even
   if no%2 == 0 {
      println!("Thank you , number is even");
   } else {
      panic!("NOT_AN_EVEN"); 
   }
   println!("End of main");
}

The above example returns an error if the value assigned to the variable is odd.

Output

thread 'main' panicked at 'NOT_AN_EVEN', main.rs:9
note: Run with `RUST_BACKTRACE=1` for a backtrace.

Result Enum and Recoverable Errors

Enum Result – <T, E> can be used to handle recoverable errors. It has two variants − OK and ErrT and E are generic type parameters. T represents the type of the value that will be returned in a success case within the OK variant, and E represents the type of error that will be returned in a failure case within the Err variant.

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

Let us understand this with the help of an example −

use std::fs::File;
fn main() {
   let f = File::open("main.jpg"); 
   //this file does not exist
   println!("{:?}",f);
}

The program returns OK(File) if the file already exists and Err(Error) if the file is not found.

Err(Error { repr: Os { code: 2, message: "No such file or directory" } })

Let us now see how to handle the Err variant.

The following example handles an error returned while opening file using the match statement

use std::fs::File;
fn main() {
   let f = File::open("main.jpg");   // main.jpg doesn't exist
   match f {
      Ok(f)=> {
         println!("file found {:?}",f);
      },
      Err(e)=> {
         println!("file not found \n{:?}",e);   //handled error
      }
   }
   println!("end of main");
}

NOTE − The program prints the end of the main event though the file was not found. This means the program has handled errors gracefully.

Output

file not found
Os { code: 2, kind: NotFound, message: "The system cannot find the file specified." }
end of main

Illustration

The is_even function returns an error if the number is not an even number. The main() function handles this error.

fn main(){
   let result = is_even(13);
   match result {
      Ok(d)=>{
         println!("no is even {}",d);
      },
      Err(msg)=>{
         println!("Error msg is {}",msg);
      }
   }
   println!("end of main");
}
fn is_even(no:i32)->Result<bool,String> {
   if no%2==0 {
      return Ok(true);
   } else {
      return Err("NOT_AN_EVEN".to_string());
   }
}

NOTE − Since the main function handles errors gracefully, the end of the main statement is printed.

Output

Error msg is NOT_AN_EVEN
end of main

unwrap() and expect()

The standard library contains a couple of helper methods that both enums − Result<T,E> and Option<T> implement. You can use them to simplify error cases where you really do not expect things to fail. In case of success from a method, the “unwrap” function is used to extract the actual result.

Sr.NoMethodSignature & Description
1unwrapunwrap(self): TExpects self to be Ok/Some and returns the value contained within. If it is Error None instead, it raises a panic with the contents of the error displayed.
2expectexpect(self, msg: &str): TBehaves like unwrap, except that it outputs a custom message before panicking in addition to the contents of the error.

unwrap()

The unwrap() function returns the actual result an operation succeeds. It returns a panic with a default error message if an operation fails. This function is a shorthand for a match statement. This is shown in the example below −

fn main(){
   let result = is_even(10).unwrap();
   println!("result is {}",result);
   println!("end of main");
}
fn is_even(no:i32)->Result<bool,String> {
   if no%2==0 {
      return Ok(true);
   } else {
      return Err("NOT_AN_EVEN".to_string());
   }
}
result is true
end of main

Modify the above code to pass an odd number to the is_even() function.

The unwrap() function will panic and return a default error message as shown below

thread 'main' panicked at 'called `Result::unwrap()` on 
an `Err` value: "NOT_AN_EVEN"', libcore\result.rs:945:5
note: Run with `RUST_BACKTRACE=1` for a backtrace

expect()

The program can return a custom error message in case of panic. This is shown in the following example −

use std::fs::File;
fn main(){
   let f = File::open("pqr.txt").expect("File not able to open");
   //file does not exist
   println!("end of main");
}

The function expect() is similar to unwrap(). The only difference is that a custom error message can be displayed using expect.

Output

thread 'main' panicked at 'File not able to open: Error { repr: Os 
{ code: 2, message: "No such file or directory" } }', src/libcore/result.rs:860
note: Run with `RUST_BACKTRACE=1` for a backtrace.

Next Topic – Click Here

This Post Has One Comment

Leave a Reply