Introduction to Error Handling in RUST

Like most programming languages, Rust encourages the programmer to handle errors in a particular way. If we talk in a general way Rust gives you an acknowledgment about an error and gives you an option to define some action for it before compile. This makes your program more robust by ensuring that you’ll discover errors and handle them appropriately.

Rust group errors into two major categories: Recoverable and Unrecoverable errors. For a recoverable error, such as a “file not found” error, the user can recover by retrying. Unrecoverable errors are mostly a symptom of bugs, like trying to access a location beyond the end of an array.

Most languages don’t distinguish between these two types of errors and handle both in the same way, using mechanisms such as exceptions. Rust doesn’t have exceptions.

Unrecoverable Error –

Unrecoverable Error is an error which is detected, and the programmer can not handle it. When such kind of error occurs, then panic! macro is executed. The panic! prints the failure message. The panic! macro unwinds cleans up the stack and then quit.

example : –

Filename: src/main.rs

fn main()
{
    let v = vec![120,300,50];
    let value = v[5];
}
$ cargo run
Compiling adder v0.1.0 (file:///home/knoldus/Rustprojects/adder)
Finished dev [unoptimized + debuginfo] target(s) in 0.42s
Running `target/debug/adder`
thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 5', /checkout/src/libcore/slice/mod.rs:2085:10
note: Run with `RUST_BACKTRACE=1` for a backtrace.

To protect your program from this sort of vulnerability like if you try to read an element at an index that doesn’t exist, Rust will stop execution and refuse to continue.

In other languages, like C and C++, it will attempt to give you exactly what you have asked for. In this situation, even though the value doesn’t exist you will get whatever is at that location in memory that would correspond to that element in the vector, even though the memory doesn’t belong to the vector. This is called a buffer overread and can lead to security vulnerabilities if an attacker is able to manipulate the index in such a way as to read data that they are not allowed to.

Recoverable Error –

Recoverable errors are those errors which are not very serious to stop the program entirely. The errors which can be handled are known as recoverable errors. It is represented by Result<T, E>. The Result<T, E> is an enum consists of two variants, i.e., OK and Err (describes the possible error).

OK: The ‘T’ is a type of value which returns the OK variant in the success case. It is an expected outcome.

Err: The ‘E’ is a type of error which returns the ERR variant in the failure. It is an unexpected outcome.

example:-

Filename: src/main.rs

use std::fs::File;

fn main() {
    let x = try!(File::create("my_file.txtx"));
}
error[E0308]: mismatched types
--> src/main.rs:5:17
|
5 | let x:u32 = File::create("my_file.txtx");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected u32, found enum `std::result::Result`
|
= note: expected type `u32`
found type `std::result::Result<std::fs::File, std::io::Error>`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.
error: Could not compile `adder`.

The above example will not compile as the return type of the File::create  function is a Result<T, E>. The generic parameter T has been filled in here with the type of the success value of std::fs::File, which is a file handle. The type of E used in the error value is std::io::Error. So in the next example, we can see how to handle an error.

use std::fs::File;

fn main() {
    let f = File::open("hello.txt");

    let f = match f {
        Ok(file) => file,
        Err(error) => {
            panic!("There was a problem opening the file: {:?}", error)
        },
    };
}

In this, if it finds the hello.txt file then it will execute Ok. otherwise, it will give Error that main thread is panicked is shown below.

Compiling adder v0.1.0 (file:///home/knoldus/Rustprojects/adder)
warning: unused variable: `f`
--> src/main.rs:6:9
|
6 | let f = match f {
| ^ help: consider using `_f` instead
|
= note: #[warn(unused_variables)] on by default

Finished dev [unoptimized + debuginfo] target(s) in 0.38s
Running `target/debug/adder`
thread 'main' panicked at 'There was a problem opening the file: Os { code: 2, kind: NotFound, message: "No such file or directory" }', src/main.rs:9:13
note: Run with `RUST_BACKTRACE=1` for a backtrace.

 

References :

 

Leave a Reply

%d bloggers like this: