Home Rust Programming Result in Rust Programming Language

Result in Rust Programming Language

In Rust programming language, a Result is a data type typically returned from the function for which the result of the computation is either successful or unsuccessful.

The Result<T, E> can have one of two outcomes:

  • Ok(T): An element T was found
  • Err(E): An error was encountered with an element E

Here the T and E type parameters representing the types of successful and unsuccessful results.

The result is an enum type that is defined as follows:

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

Let’s understand the Result type with an example. Imagine we have a function to divide a number num1 by num2 as let res = (num1/num2);

Will this division always be successful?

No. Imagine if the num2 turns out to be a 0 and we already know that we can’t divide any number by 0. Hence in such cases, we can return Result<T, E> from that function. Let’s see the example code for the same:

fn divide(num1: i32, num2: i32) -> Result<i32, String>{
    if num2 == 0 {
        Err("Division by zero".into())
    }
    else{
        Ok(num1/num2)
    }
}

fn main(){
    
    let result_success = divide(9, 3);
    match result_success{
        Ok(res) => println!("The result of division is {}", res),
        Err(e) => println!("Error: {}", e),
    }
    
    let result_fail = divide(9, 0);
    match result_fail{
        Ok(res) => println!("The result of division is {}", res),
        Err(e) => println!("Error: {}", e),
    }
}
Output
The result of division is 3 
Error: Division by zero

Here our divide function will return the result of division as i32 in case of successful division but if the division is not possible (if the num2 is 0), in such case we will return a string message representing that division is not possible.

While working with rust you will often come across many methods that Result type. One example of this is the parse() method. It might not always be possible to parse a string slice into the other type, so parse()returns a Result indicating possible failure. Another example is File::open("path_of_file"). This function attempts to open a file in read-only mode and if the result is Ok it will return a file handle else it will return an Err if path does not exist.

Result Unwrapping

The Result unwrapping is done via unwrap(), unwrap_or(), unwrap_or_else() function in rust, which consumes the self value and returns the contained Ok() value. Let’s understand the difference between different ways of unwrapping a Result type.

    1. unwrap(): If the result type is Ok() it returns the contained Ok value else panic if the value is an Err, with a panic message provided by the Err’s value. Because this function may panic, its use is generally discouraged
      let x: Result<&str, &str> = Ok("IntMain")
      assert_eq!(x.unwrap(), "IntMain");
      
      let y: Result<&str, &str> = Err("Intentional Failure");
      y.unwrap();    //panics with "Intentional Failure" message
    2. unwarp_or(): If the result type is Ok() it returns the contained Ok value else returns provided default value if the value is an Err.
      let default = "RustLang"; 
      
      let x: Result<&str, &str> = Ok("IntMain");
      assert_eq!(x.unwrap_or(default), "IntMain"); 
      
      let y: Result<&str, &str> = Err("Intentional Failure"); 
      //returns the default value 
      assert_eq!(y.unwrap_or(default), default);
    3. unwrap_or_else(): If the result is Ok() it returns the contained Ok value else allows us to provide a fallback value to use in case the result is an Err variant. The fallback value is obtained by passing a closure to unwrap_or_else, which is executed if the Result is an Err.
      fn count_len(x: &str) -> usize { x.len() }
      
      let x: Result<&str, &str> = Ok("IntMain") 
      assert_eq!(x.unwrap_or_else(count_len), "IntMain"); 
      
      //returns the closure obtained value 
      assert_eq!(Err("Intentional Failure").unwrap_or_else(count_len), 19);

Result Matching

Result unwrapping can be risky, hence rust provides a powerful pattern-matching implementation. The match keyword in Rust provides pattern matching on values, allowing you to check for specific cases and execute different codes for each case. It’s commonly used with the Result type to handle the successful (Ok) and error (Err) variants.

Here’s an example of using match with a Result:

fn main() {

    let result: Result<&str, &str> = Ok("IntMain"); 
    match result { 
        Ok(value) => println!("The value is: {}", value), 
        Err(err) => println!("An error occurred: {}", err), 
    }

    let result: Result<&str, &str> = Err("Intentional Failure"); 
    match result { 
        Ok(value) => println!("The value is: {}", value), 
        Err(err) => println!("An error occurred: {}", err), 
    }
}
Output
The value is: IntMain 
An error occurred: Intentional Failure

Conclusion

The Result type in Rust is used to represent the result of a computation that may fail or succeed. It’s defined as an enumeration type with two variants: Ok and Err. The Ok variant indicates success and contains the result value, while the Err variant indicates failure and contains an error value. The Result type is commonly used in Rust for error handling, where the function returns a Result indicating either success or failure, along with an error message.

LEAVE A REPLY

Please enter your comment!
Please enter your name here