Rust Lesson 24 – Option And Result | Dataplexa

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 exists
  • None — 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 value
  • is_some() and is_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) — success
  • Err(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.