Option & Result Types in Rust
In this lesson, you will learn about two of the most important enums in Rust: Option and Result. These types are central to Rust’s philosophy of writing safe and reliable code.
Unlike many languages that use null or unchecked exceptions,
Rust forces you to explicitly handle missing values and errors.
Why Option and Result Exist
Rust does not have null.
Instead, it uses enums to represent situations where:
- A value may or may not exist
- An operation may succeed or fail
This design prevents common runtime bugs such as null pointer exceptions.
1) The Option<T> Type
Option<T> represents an optional value.
It can have one of two variants:
Some(value)— when a value existsNone— when a value is absent
Creating Option Values
let some_number = Some(10);
let no_number: Option = None;
Rust requires explicit handling of both cases.
Using match with Option
The safest way to work with Option is using match.
let value = Some(5);
match value {
Some(v) => println!("Value is {}", v),
None => println!("No value found"),
}
Using if let with Option
When you only care about the Some case, if let is more concise.
let name = Some("Rust");
if let Some(n) = name {
println!("Name is {}", n);
}
Common Option Methods
Rust provides useful methods to work with Option.
unwrap()— extracts the value (panics if None)unwrap_or()— provides a default valueis_some()andis_none()
let score = None;
let final_score = score.unwrap_or(0);
println!("{}", final_score);
2) The Result<T, E> Type
Result<T, E> is used for operations that may fail.
It has two variants:
Ok(value)— successErr(error)— failure
Creating and Matching Result
fn divide(a: i32, b: i32) -> Result {
if b == 0 {
Err("Cannot divide by zero".to_string())
} else {
Ok(a / b)
}
}
match divide(10, 2) {
Ok(result) => println!("Result: {}", result),
Err(e) => println!("Error: {}", e),
}
Using unwrap and expect with Result
You can use unwrap() or expect(), but only when you are sure
the operation will succeed.
let result = divide(20, 4).unwrap();
println!("{}", result);
The ? Operator (Error Propagation)
The ? operator simplifies error handling by returning early if an error occurs.
fn safe_divide(a: i32, b: i32) -> Result {
let result = divide(a, b)?;
Ok(result)
}
Option vs Result (Key Difference)
Although both are enums, they serve different purposes:
- Option: value may or may not exist
- Result: operation may succeed or fail with an error
📝 Practice Exercises
Exercise 1
Create an Option<i32> with a value and print it using match.
Exercise 2
Create a function that returns None when a number is negative.
Exercise 3
Write a function that returns a Result for division.
Exercise 4
Use the ? operator to simplify error handling.
✅ Practice Answers
Answer 1
let val = Some(7);
match val {
Some(v) => println!("{}", v),
None => println!("No value"),
}
Answer 2
fn check(n: i32) -> Option {
if n < 0 {
None
} else {
Some(n)
}
}
Answer 3
fn divide(a: i32, b: i32) -> Result {
if b == 0 {
Err("Division by zero".to_string())
} else {
Ok(a / b)
}
}
Answer 4
fn compute(a: i32, b: i32) -> Result {
let result = divide(a, b)?;
Ok(result)
}
What’s Next?
In the next lesson, you will learn about Traits — Rust’s way of defining shared behavior across different types.