14.5 Practical Examples

Let’s examine how Option<T> is applied in typical programming tasks.

14.5.1 Retrieving Data from Collections

Hash maps and other collections often return Option from lookup operations.

use std::collections::HashMap;

fn main() {
    let mut scores = HashMap::new();
    scores.insert("Alice", 100);
    scores.insert("Bob", 95);

    let alice_score_option = scores.get("Alice"); // Returns Option<&i32>
    match alice_score_option {
        Some(&score) => println!("Alice's score: {}", score),
        // Note the &score pattern
        None => println!("Alice not found."),
    }

    // Using map to process the score if present
    let bob_score_msg = scores.get("Bob") // Option<&i32>
                  .map(|&score| format!("Bob's score: {}", score)) // Option<String>
                  .unwrap_or_else(|| "Bob not found.".to_string()); // String
    println!("{}", bob_score_msg);

    let charlie_score = scores.get("Charlie");
    if charlie_score.is_none() {
        println!("Charlie's score is not available.");
    }
}

Output:

Alice's score: 100
Bob's score: 95
Charlie's score is not available.

14.5.2 Optional Struct Fields

Representing optional configuration or data within structs is a common use case.

struct UserProfile {
    user_id: u64,
    display_name: String,
    email: Option<String>, // Email might not be provided
    location: Option<String>, // Location might be optional
}

impl UserProfile {
    fn new(id: u64, name: String) -> Self {
        UserProfile {
            user_id: id,
            display_name: name,
            email: None,
            location: None,
        }
    }

    fn with_email(mut self, email: String) -> Self {
        self.email = Some(email);
        self
    }

    fn with_location(mut self, location: String) -> Self {
        self.location = Some(location);
        self
    }
}

fn main() {
    let user1 = UserProfile::new(101, "Admin".to_string())
                .with_email("admin@example.com".to_string());

    println!("User ID: {}", user1.user_id);
    println!("Display Name: {}", user1.display_name);

    // Use as_deref() to convert Option<String> to Option<&str> before unwrap_or
    // This avoids moving the String out and works well with &str default.
    println!("Email: {}", user1.email.as_deref().unwrap_or("Not provided"));

    // Alternatively, use unwrap_or_else for a String default
    println!("Location: {}", user1.location.unwrap_or_else(|| "Unknown".to_string()));
}

Output:

User ID: 101
Display Name: Admin
Email: admin@example.com
Location: Unknown