Menu
Coddy logo textTech

The Option Enum Revisited

Part of the Object Oriented Programming section of Coddy's Rust journey — lesson 17 of 61.

You've already encountered Option<T> in Rust—it's the standard enum for representing values that might or might not exist. Now let's look at it through an OOP lens: Option isn't just an enum, it's a type with powerful built-in methods that let you handle nullable values safely and elegantly.

Instead of matching every time, you can use methods directly on Option values. The unwrap_or method returns the inner value if it exists, or a default you provide:

let maybe_name: Option<String> = Some(String::from("Alice"));
let name = maybe_name.unwrap_or(String::from("Guest"));
println!("{}", name);  // Alice

let empty: Option<String> = None;
let fallback = empty.unwrap_or(String::from("Guest"));
println!("{}", fallback);  // Guest

The map method transforms the inner value if it exists, leaving None unchanged. This lets you chain operations without constantly checking for None:

let maybe_num: Option<i32> = Some(5);
let doubled = maybe_num.map(|x| x * 2);
println!("{:?}", doubled);  // Some(10)

let nothing: Option<i32> = None;
let still_nothing = nothing.map(|x| x * 2);
println!("{:?}", still_nothing);  // None

These methods embody OOP principles—the Option type encapsulates the concept of "maybe a value" and provides a clean interface for working with it. You call methods on the object rather than writing external logic to inspect it.

challenge icon

Challenge

Easy

Let's build a user profile system that gracefully handles optional data using Option methods. Users might have a nickname set, or they might not—your system will handle both cases elegantly without explicit matching.

You'll create two files to organize your code:

  • profile.rs: Define a public Profile struct with two fields: username (String) and nickname (Option<String>). Implement:
    • A new constructor that takes a username and an optional nickname
    • A display_name method that returns the nickname if it exists, or the username as a fallback—use unwrap_or to handle this
    • A formatted_nickname method that returns an Option<String> with the nickname wrapped in brackets (like "[CoolNick]") if it exists, or None if there's no nickname—use map to transform the value
  • main.rs: Bring in your profile module and create two profiles—one with a nickname and one without. Demonstrate both methods on each profile to show how Option methods handle present and absent values differently.

For the formatted_nickname method, use debug formatting ({:?}) when printing since it returns an Option.

Your output should follow this exact format, showing both profiles:

Display name: {name}
Formatted nickname: {option}
Display name: {name}
Formatted nickname: {option}

For example, with a profile for username "alice" with nickname "Ace", and another for username "bob" with no nickname, the output would be:

Display name: Ace
Formatted nickname: Some("[Ace]")
Display name: bob
Formatted nickname: None

You will receive three inputs: the first username, the first user's nickname, and the second username (who has no nickname).

Cheat sheet

The Option<T> type provides methods for working with optional values without explicit matching.

The unwrap_or method returns the inner value if it exists, or a default value if it's None:

let maybe_name: Option<String> = Some(String::from("Alice"));
let name = maybe_name.unwrap_or(String::from("Guest"));
println!("{}", name);  // Alice

let empty: Option<String> = None;
let fallback = empty.unwrap_or(String::from("Guest"));
println!("{}", fallback);  // Guest

The map method transforms the inner value if it exists, leaving None unchanged:

let maybe_num: Option<i32> = Some(5);
let doubled = maybe_num.map(|x| x * 2);
println!("{:?}", doubled);  // Some(10)

let nothing: Option<i32> = None;
let still_nothing = nothing.map(|x| x * 2);
println!("{:?}", still_nothing);  // None

These methods allow chaining operations on Option values without constantly checking for None.

Try it yourself

mod profile;

use profile::Profile;

fn main() {
    // Read inputs
    let mut input1 = String::new();
    std::io::stdin().read_line(&mut input1).expect("Failed to read line");
    let username1 = input1.trim().to_string();

    let mut input2 = String::new();
    std::io::stdin().read_line(&mut input2).expect("Failed to read line");
    let nickname1 = input2.trim().to_string();

    let mut input3 = String::new();
    std::io::stdin().read_line(&mut input3).expect("Failed to read line");
    let username2 = input3.trim().to_string();

    // TODO: Create first profile with username1 and nickname1 (Some)
    
    // TODO: Create second profile with username2 and no nickname (None)
    
    // TODO: Print display_name and formatted_nickname for first profile
    // Use {:?} for formatted_nickname since it returns an Option
    
    // TODO: Print display_name and formatted_nickname for second profile
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming