Improve your Error Handling in Rust
Rust has very cool functionalities you can implement to improve your code and your Error Handling.
Table of contents:
Rust shortcut for propagating errors
Use a custom ErrorKind
Alias is cool for 'Result'
Rust shortcut for propagating errors
This functionality is so basic in Rust Error Handling that everyone should know it. I included it in this post if there are readers who are learning Rust.
'?' Rust shortcut
'?' is a Rust shortcut for propagating errors, you can place it just after a 'Result' value. If the 'Result' value is 'Ok' the program will continue and you will get access to the value. But if the 'Result' value is an Error, the 'Err' value will be automatically returned and the value will be propagated to the calling function as a basic 'return'.
use std::num::ParseIntError;
// Convert a string to an u64
fn my_string_to_nb(string: &str) -> Result<u64, ParseIntError>{
let my_int = string.parse::<u64>()?; // I use here the shortcut
Ok(my_int)
}
fn main() {
// 'Result<Ok>' example
let str_test = "25"; // Can be convert to u64
let res = my_string_to_nb(str_test); // I catch here the result
if let Ok(_e) = res {
println!("{}", _e); // Output: "25"
}
//'Result<Err>' example
let str_test = "Hello World!"; // Can not be convert to u64
let res = my_string_to_nb(str_test); // I catch here the result
if let Err(_e) = res {
println!("{}", _e); // Output: "invalid digit found in string"
}
}
As you see, Rust did a good job, and this shortcut is very useful. But it is more if you want to propagate an error to the first calling function for example.
// Return an error to main
fn this_return_an_error() -> Result<(), String> {
Err("This is an error!".to_string()) // Return an error
}
fn again_an_error() -> Result<(), String> {
let _ = this_return_an_error()?; // I use the shortcut
Ok(())
}
fn you_guess_what_it_does() -> Result<(), String> {
let _ = again_an_error()?; // I use the shortcut
Ok(())
}
fn main() {
let res = you_guess_what_it_does(); // And I catch the result
if let Err(_e) = res {
println!("{}", _e); // Output: "This is an error!"
}
// Whoa this is cool ;)
}
With this shortcut, you can do a more readable Error Handling!
Use a custom ErrorKind
Depending on your project and what you want to do, it can be important to have custom Errors. I use an enum to return my custom ErrorKind. If you want a description of your error, you can give a value to your type, for example, an int or a string.
#[derive(Debug)]
enum ErrorKind {
FirstError(), // No value
SecondError(String), // Give an error description
ThirdError(u8), // Give an error number
}
fn this_return_errors(nb: u32) -> Result<(), ErrorKind> {
match nb {
1 => Err(ErrorKind::FirstError()),
2 => Err(ErrorKind::SecondError("Error description".to_string())),
_ => Err(ErrorKind::ThirdError(5)),
}
}
fn main() {
let nb = 1; // You can change this value
let res = this_return_errors(nb);
if let Err(_e) = res {
println!("{:?}", _e); // Output: "FirstError"
}
}
For beginners, using custom Errors for your project is very important for more readability. You will easily see where the error comes from and a reader will understand your code. Use precise Error names!
Alias is cool for 'Result'
Now for each function, if you have a custom ErrorKind or not, you know that you will return something like that => 'Result<(), ErrorKind>'. But if you always return the same thing like 'Err(ErrorKind)' you don't need to specify it. Let me explain, you can create a 'MyResult' type which is an alias of 'Result<T, ErrorKind>' where you don't need to specify the 'Err' type.
#[derive(Debug)]
enum ErrorKind {
SomeError(),
}
// Create an alias for Result
type MyResult<T> = Result<T, ErrorKind>;
fn this_return_an_error(b: bool) -> MyResult<String> { // Wow a custom Type
match b {
true => Ok("Hello World!".to_string()),
false => Err(ErrorKind::SomeError()),
}
}
fn main() {
let res = this_return_an_error(false);
println!("{:?}", res); // Output: "Err(SomeError)"
}
This is a cool functionality, it's useful if you declare over and over the same result in a file. It's more for cosmetics but I like to use it in my code.
Conclusion
Error Handling is very important so with these tips, you can truly improve your code whether you are a beginner or not.
'?' as Rust shortcut
Custom ErrorKind
Alias for 'Result'
Thank you for reading this post and good programming session to you!