Error Handling
Master different error handling techniques in Rust, including `Result` type and `panic!` macro.
Handling Result
with match
in Rust
The Result
enum in Rust is a powerful tool for handling operations that might fail. It has two variants: Ok(T)
, which represents success and contains a value of type T
, and Err(E)
, which represents failure and contains an error value of type E
. The match
expression provides a robust and explicit way to handle both these possibilities.
Using match
for Explicit Result
Handling
The match
expression allows you to examine the Result
value and execute different code blocks based on whether it's an Ok
or an Err
. This forces you to explicitly acknowledge and handle potential errors, making your code more reliable.
fn divide(a: i32, b: i32) -> Result<i32, String> {
if b == 0 {
Err("Division by zero is not allowed".to_string())
} else {
Ok(a / b)
}
}
fn main() {
let result = divide(10, 2);
match result {
Ok(quotient) => {
println!("The quotient is: {}", quotient);
}
Err(error) => {
println!("Error: {}", error);
}
}
let another_result = divide(5, 0);
match another_result {
Ok(quotient) => {
println!("The quotient is: {}", quotient);
}
Err(error) => {
println!("Error: {}", error);
}
}
}
Explanation:
- The
divide
function attempts to divide two integers. If the divisor is zero, it returns anErr
with a descriptive error message. Otherwise, it returns anOk
containing the result of the division. - In
main
, we calldivide
and store the returnedResult
in theresult
variable. - The
match
expression then checks the value ofresult
:- If it's
Ok(quotient)
, the code block associated with theOk
pattern is executed. The value inside theOk
variant (the quotient) is bound to the variablequotient
, which can then be used within that block. - If it's
Err(error)
, the code block associated with theErr
pattern is executed. The error message is bound to the variableerror
, which can then be used to display the error.
- If it's
- The example demonstrates how the
match
handles both a successful division (10 / 2) and a division by zero.
Different Patterns and Error Handling Strategies
The match
expression can be used with more complex patterns and error handling strategies.
#[derive(Debug)]
enum MathError {
DivisionByZero,
NegativeRoot,
}
fn safe_sqrt(number: f64) -> Result<f64, MathError> {
if number < 0.0 {
Err(MathError::NegativeRoot)
} else {
Ok(number.sqrt())
}
}
fn safe_divide(a: i32, b: i32) -> Result<i32, MathError> {
if b == 0 {
Err(MathError::DivisionByZero)
} else {
Ok(a / b)
}
}
fn main() {
let sqrt_result = safe_sqrt(-1.0);
match sqrt_result {
Ok(val) => println!("Square root: {}", val),
Err(MathError::NegativeRoot) => println!("Error: Cannot take the square root of a negative number"),
Err(err) => println!("An unexpected error occurred: {:?}", err), // Fallback for other errors
}
let division_result = safe_divide(10,0);
match division_result {
Ok(val) => println!("Result of division is : {}", val),
Err(MathError::DivisionByZero) => println!("Attempted division by zero."),
Err(err) => println!("Other Errors: {:?}", err),
}
}
Explanation:
- Custom Error Types: The code defines a custom enum
MathError
to represent specific error conditions (division by zero, negative root). This makes the error handling more precise. - Specific Error Matching: The
match
expression can match on specific error variants within theErr
arm. For example,Err(MathError::NegativeRoot)
handles only the case where a negative number is passed tosafe_sqrt
. - Fallback Error Handling: The
Err(err)
case acts as a catch-all, handling any other error variants that might occur. This is important to ensure that all possible error conditions are addressed, even if you don't have specific handling for each one. The{:?}
formatter used with `println!` will print the debug representation of the error variant.
Benefits of Using match
- Exhaustive Handling: The compiler forces you to handle both
Ok
andErr
cases. - Pattern Matching: Allows you to extract values from
Ok
and match specific error variants. - Readability: Makes your code clearer and easier to understand.
- Safety: Reduces the risk of unhandled errors leading to unexpected behavior.