Pybites Logo Rust Platform

Option Combinators

Easy +2 pts

In the previous exercise, you used match to handle Option. But matching every time gets verbose:

let name: Option<String> = get_name();
let upper = match name {
    Some(n) => Some(n.to_uppercase()),
    None => None,
};

Rust provides combinators — methods that transform Options without explicit matching.

.map() — transform the inner value

let name: Option<String> = Some("alice".to_string());
let upper = name.map(|n| n.to_uppercase());
// Some("ALICE")

let empty: Option<String> = None;
let upper = empty.map(|n| n.to_uppercase());
// None — closure never runs

In Python, this is like: x.upper() if x is not None else None

.unwrap_or() — provide a default

let count: Option<i32> = None;
let value = count.unwrap_or(0);  // 0

let count: Option<i32> = Some(42);
let value = count.unwrap_or(0);  // 42

.unwrap_or_else() — lazy default

When computing the default is expensive, use a closure:

let value = count.unwrap_or_else(|| expensive_computation());

.and_then() — chain Option-returning functions

When your transformation also returns an Option:

fn find_name(id: u32) -> Option<&'static str> {
    match id {
        1 => Some("Alice"),
        2 => Some("Bob"),
        _ => None,
    }
}

let id: Option<u32> = Some(1);
let name: Option<&str> = id.and_then(find_name);
// Some("Alice")

.map() would give Option<Option<&str>>. .and_then() flattens it.

.filter() — keep only if condition holds

let age: Option<i32> = Some(25);
let adult = age.filter(|&a| a >= 18);  // Some(25)

let age: Option<i32> = Some(15);
let adult = age.filter(|&a| a >= 18);  // None

Login to see the full task and start coding.

This is a premium exercise

Log in to unlock the full exercise and start coding.

Login to access this exercise