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
Result
helps avoid the need for exceptions and makes error handling explicit. - Be careful with methods like
unwrap
that can cause a panic if theResult
isErr
. - Consider using methods like
unwrap_or
orunwrap_or_else
to provide default values and avoid panics. - When chaining multiple operations that can fail, use
and_then
to handle errors gracefully.
See Also
- Option - An enum that can contain either a value or
None
, useful for handling optional values.