Rust is a statically-typed, systems programming language that focuses on safety, speed, and concurrency. Error handling is crucial for a robust code, which can deal with unexpected situations without crashing. In Rust, there are various approaches for error handling, including the common Rust types such as Result and Option, and defining custom error types.
In this page, we'll explore more on advanced error handling in Rust.
Result and OptionThe Result and Option types in Rust provide a powerful way to handle error situations.
Here is a basic example of using Result:
fn double_number(number_str: &str) -> Result<i32, &'static str> {
match number_str.parse::<i32>() {
Ok(n) => Ok(2 * n),
Err(_) => Err("Error parsing string to number"),
}
}
fn main() {
match double_number("10") {
Ok(n) => println!("The double number is {}", n),
Err(err) => println!("Error: {}", err),
}
}
In the same way, Option is used when we have a situation where a value might or might not be present.
fn find_map_value(map: &HashMap<i32, String>, key: i32) -> Option<&String> {
map.get(&key)
}
There may be situations where you need to define your own error types. You can define them by implementing the Error trait.
use std::fmt;
use std::error::Error;
struct CustomError {
details: String
}
impl CustomError {
fn new(msg: &str) -> CustomError {
CustomError{details: msg.to_string()}
}
}
impl fmt::Display for CustomError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.details)
}
}
impl Error for CustomError {
fn description(&self) -> &str {
&self.details
}
}
After creating your own error, you can use it like other built-in errors.
Rust also provides a variety of macros that makes dealing with errors easier. The try! macro can be used to return the error from a function immediately. The ? operator is another macro that has similar utility and is often more idiomatic.
The try! macro used to be more common, but it has been mostly superseded by ?. Here is an example of ?:
fn double_number(number_str: &str) -> Result<i32, &'static str> {
let n = number_str.parse::<i32>().map_err(|_| "Error parsing string to number")?;
Ok(2 * n)
}
In this example ? will return immediately if the step before it results in an error.
When should I use Result over Option in Rust?
Result should be used when you want to carry information about what went wrong in case of an error. For Option, you simply care whether a value is present or not, and do not have any need to carry further information about the reason of absence.What is the Try trait in Rust?
Try trait is a way to define computations that can fail. It is used to support the ? operator. A type T implements Try if there's some type U such that values of T can be understood as being of type U, success type, or an error.Can I create custom Errors in Rust?
Error trait you can create custom errors in Rust. This should include implementation of the fmt::Display trait and the description method.What is the difference between try! and ? in Rust?
try! macro and ? perform the same task: they are used to propagate errors up the stack. However, the ? operator is more idiomatic and recommended in contemporary Rust. It also has slightly shorter syntax.