Recoverable Error
Rust Recoverable Errors
- Errors that can be fixed are ones that don’t require the program to terminate completely. Errors that are recoverable are those that are manageable.
- Result<T, E> serves as its representation. There are two forms of the enum Result<T, E>: OK<T> and Err<E>. It explains the potential mistake.
OK<T>: In the success situation, the value type denoted by ‘T’ yields the OK variation. It is the anticipated result.
Err<E>: An error type denoted by a ‘E’ returns the ERR variation in the event of a failure. It’s not what was supposed to happen.
Enum Result
{
OK,
Err,
}
- The enum type in the example above is called Result, and its variations are called OK<T> and Err<E>, where ‘T’ and ‘E’ represent the generic type arguments.
- The value type ‘T’ will be returned in the event of success, while the error type ‘E’ will be returned in the event of failure.
- We can utilize the Result type and functions defined in the standard library in a variety of scenarios where the success and failure results may differ since the Result contains the generic type parameters.
Example:
use std::fs::File;
fn main()
{
let f:u32 = File::open("vector.txt");
}
Output:
The Rust compiler indicates that the type does not match in the example above. While File:: open returns the Result<T, E>type, ‘f’ is an u32 type. The result above demonstrates that the error value is of type std::io:: Error while the success value is of type std::fs:: File.
Note:
- There are two possible return types for the File::open: success and failure. A file handle is returned if file:: open is successful, and an error value is returned if it is unsuccessful. This data is provided by the Result enum.
- If File:: open successful, then f will have an OK variant with the file handle; if File:: open is unsuccessful, then f will have an Err variant with the error’s details.
Match Expression to handle the Result variants.
Example:
use std::fs::File;
n main()
{
let f = File::open("vector.txt");
match f
{
Ok(file) => file,
Err(error) => {
panic!("There was a problem opening the file: {:?}", error)
},
};
Output:
Program Explanation
- Without utilizing the Result:: before OK and Err variation, we may access the enum variants directly in the case above.
- The file is returned and stored in the ‘f’ variable if the outcome is satisfactory. The operations in the file can be read or written after the match.
- The Err value is the focus of the match’s second arm. Panic! occurs and ends program execution if Result returns the Error value.
Panic on Error: unwrap()
There are numerous ways to provide different jobs with the Result<T, E>. The unwrap() method is one of the techniques. A match expression’s shorthand method is the unwrap() function. Both the match expression and the unwrap() technique operate in the same way.
The value of the OK variation is returned by the unwrap() method if the Result value is an OK variant.
The panic! macro is called by the unwrap() method if the Result value is an Err variant.
Example:
use std::fs::File;
fn main()
{
File::open("hello.txt").unwrap();
}
Output:
In the example above, the panic macro is automatically called by the unwrap() method, and panic! displays the error information.
Panic on Error: expect()
- The expect() method functions similarly to the unwrap() method, in that they both call panic! to show the error details.
- The expect() function receives the error message as a parameter, whereas the unwrap() method does not; this is the difference between the two methods. Thus, it is easy to track the panic! source when using the expect() method.
Example:
use std::fs::File;
fn main()
{
File::open("hello.txt").expect("Not able to find the file hello.txt");
}
Output:
The error message “Not able to find the file hello.txt” is shown on the output screen in the output above, which we specify in our program. This makes it easy for us to identify the code that is causing the error. It gets challenging to identify which unwrap() function is causing panic if we have numerous of them! Panic! displays identical error messages for every problem.
Propagating Errors
The process by which errors are transferred from one function to another is known as propagating error. Errors are passed to the caller function, which has access to further information and can address the error. Suppose we have a file named as ‘a.txt’ and it contains the text “javaTpoint.” We want to create a program that performs the reading operation on this file. Let’s work on this example.
Example:
use std::io;
use std::io::Read;
use std::fs::File;
fn main()
{
let a = read_username_from_file();
print!("{:?}",a);
}
fn read_username_from_file() -> Result
{
let f = File::open("a.txt");
let mut f = match f {
Ok(file) => file,
Err(e) => return Err(e),
};
let mut s = String::new();
match f.read_to_string(&mut s) {
Ok(_) => Ok(s),
Err(e) => Err(e),
}
}
Output:
Program Explanation
- A result of type Result<T, E>, where ‘T’ is a type of String and ‘E’ is a type of io:Error, is returned by the read_username_from_file() method.
- An OK value holding a String is returned if the function is successful, and an Err value is returned if it is unsuccessful.
- The File:: open function is called at the beginning of this function. In the event that the File:: open function is successful, the value of the file handle is stored in variable f. If the function fails, the second arm of the match will return the Err value.
- Should the File:: open function be successful, a String variable will be created. The file’s text is returned by the read_to_string() method if it is successful; if not, error information is returned.
- Assume that the text “javaTpoint” is present in an external file called “a.text.” As a result, this application opens the file “a.text” and shows its contents.
Shortcut for propagating the errors: the '?' operator
The code is shorter when the ‘?’ operator is used. Since the ‘?’ operator replaces the match expressions, it functions in the same manner as the match expressions. Suppose we have a file named as ‘a.txt’ and it contains the text “javaTpoint.” We want to create a program that performs the reading operation on this file. Let’s work on this example.
Example:
use std::io;
use std::io::Read;
use std::fs::File;
fn main()
{
let a = read_username_from_file();
print!("{:?}",a);
}
fn read_username_from_file() -> Result
{
let mut f = File::open("a.txt")?;
let mut s = String::new();
f.read_to_string(&mut s)?;
Ok(s)
}
Output:
The ‘?’ operator appears before the Result value type in the example above. In the event that the result is an error, the error information is returned; otherwise, the value of the OK variation is returned.
Difference b/w '?' operator & match expression
- The ‘from’ function is specified in the from trait in the standard library, and the errors that are utilized with the ‘?’ operator flow via it.
- The ‘from’ function is called by the ‘?’ operator, and it is this function that changes the error type to the error type specified in the current function’s return type.
- The value of Err is returned by the ‘?’ operator at the conclusion of any function if an error occurs, and the value of OK is returned otherwise.
- It simplifies the function’s implementation.
Chaining method calls after the '?' operator
Even further, we can reduce a program’s code by utilizing the chaining method calls after the ‘?’ operator.
Example:
use std::io;
use std::io::Read;
use std::fs::File;
fn main()
{
let a = read_username_from_file();
print!("{:?}",a);
}
fn read_username_from_file() -> Result
{
let mut s = String::new();
File::open("a.txt")?.read_to_string(&mut s)?;
Ok(s)
}
Output:
Program Explanation
The call to read_to_string() in the example above is chained to the outcome of File::open(“a.txt”)? The ‘?’ operator is appended to the end of the read_to_string() call. If both read_to_string() and File::open(“a.txt”) are successful, it produces an OK value; if not, it returns an error value.
Limitation of '?' operator
Only functions that return a value of the Result type can utilize the ‘?’ operator. The match expression and the ‘?’ operator function in a comparable manner. Only the Result return type is compatible with the match expression.
Example:
use std::fs::File;
fn main()
{
let f = File::open("a.txt")?;
}