Skip to main content
Unlike unrecoverable errors, which force your program to halt immediately, recoverable errors allow you to gracefully handle issues while keeping your application running smoothly. In this lesson, we will explore both basic and advanced error handling patterns in Rust using recoverable errors. Recoverable errors occur when an operation might fail, but the failure can be managed. In Rust, these errors are represented by the standard library’s Result and Option types. The Result type is used for operations that can either succeed (returning an Ok variant) or fail (returning an Err variant). The Option type is useful when a value may be absent, represented by either Some (indicating a valid value) or None. This powerful type system helps prevent common bugs by forcing developers to handle potential errors explicitly.
The image explains "Recoverable Errors" using two concepts: Result<T, E>, which indicates success with Ok(T) or failure with Err(E), and Option<T>, which contains a value with Some(T) or indicates absence with None.

The Result Type in Detail

The Result type is the primary tool for managing recoverable errors in Rust. Consider the following example where the divide function divides two numbers. If the denominator is zero, the function returns an error with an appropriate message; otherwise, it wraps the result in an Ok.
When executed, the output will be:
This design compels you to consider potential failure points and handle them proactively.

Propagating Errors with the Question Mark Operator

The question mark operator (?) simplifies error handling in functions that return a Result or Option. When used, it checks the value: if it is Ok or Some, it unwraps and returns the inner value; if it is an Err or None, it returns early from the function with that error value. Consider the refactored example below using the question mark operator:
The output from this example is:
In this code, if the divide function returns an error, the ? operator causes an immediate return from the calculate function, skipping any remaining lines. You can chain multiple operations with the ? operator. For example, consider a scenario where you first read a username and then validate it:
If either read_username or validate_username fails, the error is immediately returned from the main function.

Handling Errors with unwrap_or_else

The unwrap_or_else method offers fallback behavior when dealing with Result or Option types that encounter an error or an absence of a value. This method is ideal for logging errors, returning default values, or computing a fallback value based on the error.

Example with Option

In this example, the Option value is None, so the closure passed to unwrap_or_else executes and returns a default value:
The output is:

Example with Result

For a Result, the closure receives the error as an argument. In this scenario, the operation fails, and the closure provides a fallback value:
The output will be:

Best Practices for Handling Recoverable Errors

When working with recoverable errors in Rust, keep these best practices in mind:
  1. Use Result for operations that might fail.
  2. Leverage the question mark operator (?) to streamline error propagation.
  3. Provide clear, meaningful error messages.
  4. Utilize combinators like map, and_then, and unwrap_or_else to handle errors gracefully.
  5. Handle errors at appropriate points in your code to avoid excessive error propagation.
For a quick reference on Rust error handling, consult the Rust Error Handling Guide.
Consider the following consolidated example, which demonstrates the use of both Result and Option along with the unwrap_or_else method:
The console output after running the program is:
By following these techniques, you ensure your Rust applications can handle errors robustly, allowing them to continue operating smoothly even under unexpected conditions.

Watch Video