Introduction
The Result class in Rust is an enum that represents either success (Ok) or failure (Err). It is commonly used for error handling and returning results from functions that can fail. The Result enum has two variants: Ok(T) for successful results and Err(E) for errors.
Using Result
The Result class can be used to handle operations that may fail, providing a way to propagate errors up the call stack. Here is a basic example:
fn divide(a: i32, b: i32) -> Result<i32, &'static str> {
if b == 0 {
Err("Cannot divide by zero")
} else {
Ok(a / b)
}
}
fn main() {
match divide(4, 2) {
Ok(result) => println!("Result: {}", result),
Err(e) => println!("Error: {}", e),
}
match divide(4, 0) {
Ok(result) => println!("Result: {}", result),
Err(e) => println!("Error: {}", e),
}
}
// Output:
// Result: 2
// Error: Cannot divide by zero
Key Methods
Below are some of the key methods exposed by the Result class:
unwrap
Returns the contained Ok value or panics if the value is Err.
fn main() {
let result: Result<i32, &str> = Ok(5);
println!("Unwrapped value: {}", result.unwrap());
// Uncommenting the following line will cause a panic
// let error: Result<i32, &str> = Err("Error");
// println!("Unwrapped value: {}", error.unwrap());
}
// Output: Unwrapped value: 5
unwrap_or
Returns the contained Ok value or a provided default if the value is Err.
fn main() {
let result: Result<i32, &str> = Ok(5);
let error: Result<i32, &str> = Err("Error");
println!("Value or default: {}", result.unwrap_or(0));
println!("Value or default: {}", error.unwrap_or(0));
}
// Output:
// Value or default: 5
// Value or default: 0
map
Applies a function to the contained Ok value (if any) and returns a Result with the result.
fn main() {
let result: Result<i32, &str> = Ok(5);
let new_result = result.map(|x| x * 2);
println!("Mapped value: {:?}", new_result);
}
// Output: Mapped value: Ok(10)
and_then
Applies a function to the contained Ok value (if any) that returns a Result, and flattens the result.
fn main() {
let result: Result<i32, &str> = Ok(5);
let new_result = result.and_then(|x| Ok(x * 2));
println!("Mapped and flattened value: {:?}", new_result);
}
// Output: Mapped and flattened value: Ok(10)
is_ok / is_err
Returns true if the Result is Ok / Err, respectively.
fn main() {
let result: Result<i32, &str> = Ok(5);
let error: Result<i32, &str> = Err("Error");
println!("Is result ok? {}", result.is_ok());
println!("Is error ok? {}", error.is_ok());
println!("Is result err? {}", result.is_err());
println!("Is error err? {}", error.is_err());
}
// Output:
// Is result ok? true
// Is error ok? false
// Is result err? false
// Is error err? true
Example Usage
Example 1: Basic Usage
fn divide(a: i32, b: i32) -> Result<i32, &'static str> {
if b == 0 {
Err("Cannot divide by zero")
} else {
Ok(a / b)
}
}
fn main() {
match divide(4, 2) {
Ok(result) => println!("Result: {}", result),
Err(e) => println!("Error: {}", e),
}
match divide(4, 0) {
Ok(result) => println!("Result: {}", result),
Err(e) => println!("Error: {}", e),
}
}
// Output:
// Result: 2
// Error: Cannot divide by zero
Example 2: Unwrapping Values
fn main() {
let result: Result<i32, &str> = Ok(5);
println!("Unwrapped value: {}", result.unwrap());
// Uncommenting the following line will cause a panic
// let error: Result<i32, &str> = Err("Error");
// println!("Unwrapped value: {}", error.unwrap());
}
// Output: Unwrapped value: 5
Example 3: Providing Default Values
fn main() {
let result: Result<i32, &str> = Ok(5);
let error: Result<i32, &str> = Err("Error");
println!("Value or default: {}", result.unwrap_or(0));
println!("Value or default: {}", error.unwrap_or(0));
}
// Output:
// Value or default: 5
// Value or default: 0
Example 4: Mapping Values
fn main() {
let result: Result<i32, &str> = Ok(5);
let new_result = result.map(|x| x * 2);
println!("Mapped value: {:?}", new_result);
}
// Output: Mapped value: Ok(10)
Example 5: Chaining Result Operations
fn main() {
let result: Result<i32, &str> = Ok(5);
let new_result = result.and_then(|x| Ok(x * 2));
println!("Mapped and flattened value: {:?}", new_result);
}
// Output: Mapped and flattened value: Ok(10)
Considerations
- Using
Resulthelps avoid the need for exceptions and makes error handling explicit. - Be careful with methods like
unwrapthat can cause a panic if theResultisErr. - Consider using methods like
unwrap_ororunwrap_or_elseto provide default values and avoid panics. - When chaining multiple operations that can fail, use
and_thento handle errors gracefully.
See Also
- Option - An enum that can contain either a value or
None, useful for handling optional values.
