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 elementT
was foundErr(E)
: An error was encountered with an elementE
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.
- unwrap(): If the result type is
Ok()
it returns the containedOk
value else panic if the value is anErr
, with a panic message provided by theErr
’s value. Because this function may panic, its use is generally discouragedlet 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
- unwarp_or(): If the result type is
Ok()
it returns the containedOk
value else returns provided default value if the value is anErr
.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);
- unwrap_or_else(): If the result is
Ok()
it returns the containedOk
value else allows us to provide a fallback value to use in case the result is anErr
variant. The fallback value is obtained by passing a closure tounwrap_or_else
, which is executed if theResult
is anErr
.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);
- unwrap(): If the result type is
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.