Rust Guide > Documentation > Collections > Option

Introduction

The Option class in Rust is an enum that represents either a value or the absence of a value. It is used extensively in Rust to handle situations where a value might be missing, providing a safer alternative to null references. The Option enum has two variants: Some(T) and None.

Using Option

The Option class can be used to create and handle optional values. Here is a basic example:

fn main() {
    let some_value: Option<i32> = Some(5);
    let no_value: Option<i32> = None;

    match some_value {
        Some(v) => println!("Value: {}", v),
        None => println!("No value"),
    }

    match no_value {
        Some(v) => println!("Value: {}", v),
        None => println!("No value"),
    }
}
// Output:
// Value: 5
// No value

Key Methods

Below are some of the key methods exposed by the Option class:

unwrap

Returns the contained value or panics if the value is None.

fn main() {
    let some_value: Option<i32> = Some(5);
    println!("Unwrapped value: {}", some_value.unwrap());

    // Uncommenting the following line will cause a panic
    // let no_value: Option<i32> = None;
    // println!("Unwrapped value: {}", no_value.unwrap());
}
// Output: Unwrapped value: 5

unwrap_or

Returns the contained value or a provided default if the value is None.

fn main() {
    let some_value: Option<i32> = Some(5);
    let no_value: Option<i32> = None;

    println!("Value or default: {}", some_value.unwrap_or(0));
    println!("Value or default: {}", no_value.unwrap_or(0));
}
// Output:
// Value or default: 5
// Value or default: 0

map

Applies a function to the contained value (if any) and returns an Option with the result.

fn main() {
    let some_value: Option<i32> = Some(5);
    let no_value: Option<i32> = None;

    let new_value = some_value.map(|v| v * 2);
    let new_none = no_value.map(|v| v * 2);

    println!("Mapped value: {:?}", new_value);
    println!("Mapped value: {:?}", new_none);
}
// Output:
// Mapped value: Some(10)
// Mapped value: None

and_then

Applies a function to the contained value (if any) that returns an Option, and flattens the result.

fn main() {
    let some_value: Option<i32> = Some(5);
    let no_value: Option<i32> = None;

    let new_value = some_value.and_then(|v| Some(v * 2));
    let new_none = no_value.and_then(|v| Some(v * 2));

    println!("Mapped and flattened value: {:?}", new_value);
    println!("Mapped and flattened value: {:?}", new_none);
}
// Output:
// Mapped and flattened value: Some(10)
// Mapped and flattened value: None

is_some / is_none

Returns true if the Option is Some / None, respectively.

fn main() {
    let some_value: Option<i32> = Some(5);
    let no_value: Option<i32> = None;

    println!("Is some_value some? {}", some_value.is_some());
    println!("Is no_value some? {}", no_value.is_some());
    println!("Is some_value none? {}", some_value.is_none());
    println!("Is no_value none? {}", no_value.is_none());
}
// Output:
// Is some_value some? true
// Is no_value some? false
// Is some_value none? false
// Is no_value none? true

Example Usage

Example 1: Basic Usage

fn main() {
    let some_value: Option<i32> = Some(5);
    let no_value: Option<i32> = None;

    match some_value {
        Some(v) => println!("Value: {}", v),
        None => println!("No value"),
    }

    match no_value {
        Some(v) => println!("Value: {}", v),
        None => println!("No value"),
    }
}
// Output:
// Value: 5
// No value

Example 2: Unwrapping Values

fn main() {
    let some_value: Option<i32> = Some(5);
    println!("Unwrapped value: {}", some_value.unwrap());

    // Uncommenting the following line will cause a panic
    // let no_value: Option<i32> = None;
    // println!("Unwrapped value: {}", no_value.unwrap());
}
// Output: Unwrapped value: 5

Example 3: Providing Default Values

fn main() {
    let some_value: Option<i32> = Some(5);
    let no_value: Option<i32> = None;

    println!("Value or default: {}", some_value.unwrap_or(0));
    println!("Value or default: {}", no_value.unwrap_or(0));
}
// Output:
// Value or default: 5
// Value or default: 0

Example 4: Mapping Values

fn main() {
    let some_value: Option<i32> = Some(5);
    let no_value: Option<i32> = None;

    let new_value = some_value.map(|v| v * 2);
    let new_none = no_value.map(|v| v * 2);

    println!("Mapped value: {:?}", new_value);
    println!("Mapped value: {:?}", new_none);
}
// Output:
// Mapped value: Some(10)
// Mapped value: None

Example 5: Chaining Option Operations

fn main() {
    let some_value: Option<i32> = Some(5);
    let no_value: Option<i32> = None;

    let new_value = some_value.and_then(|v| Some(v * 2));
    let new_none = no_value.and_then(|v| Some(v * 2));

    println!("Mapped and flattened value: {:?}", new_value);
    println!("Mapped and flattened value: {:?}", new_none);
}
// Output:
// Mapped and flattened value: Some(10)
// Mapped and flattened value: None

Considerations

  • Using Option helps avoid null pointer exceptions and makes the absence of a value explicit.
  • Be careful with methods like unwrap that can cause a panic if the Option is None.
  • Consider using methods like unwrap_or or unwrap_or_else to provide default values and avoid panics.

See Also

  • Result - An enum for error handling that represents either success (Ok) or failure (Err).
  • Vec - A growable, dynamically-sized collection.
  • Option - The official documentation for the Option enum.