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.

