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
EasyLet'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 publicProfilestruct with two fields:username(String) andnickname(Option<String>). Implement:- A
newconstructor that takes a username and an optional nickname - A
display_namemethod that returns the nickname if it exists, or the username as a fallback—useunwrap_orto handle this - A
formatted_nicknamemethod that returns anOption<String>with the nickname wrapped in brackets (like"[CoolNick]") if it exists, orNoneif there's no nickname—usemapto transform the value
- A
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 howOptionmethods 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: NoneYou 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
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Methods and Behavior
Intro Implementation BlocksThe Self ParameterMutable MethodsAssociated FunctionsMultiple Implementation BlocksMethod ChainingRecap - Rectangle Actions4Project: Virtual Pet
Defining the PetFeeding the Pet7Standard Traits
The Debug TraitThe Display TraitClone and CopyEquality TraitsRecap - Printable Point10Project: Document System
The Draw TraitText Component2Encapsulation and Modules
Modules BasicsThe Public KeywordPrivate FieldsGettersSettersRecap - Secure Locker5Generics
Generic StructsGeneric MethodsMultiple Generic TypesGeneric FunctionsRecap - Coordinate Point8Traits as Bounds
Trait Bounds SyntaxMultiple BoundsThe Where ClauseReturning Types with TraitsRecap - Generic Printer11Design Patterns in Rust
Newtype PatternCompositionThe Drop TraitFrom and IntoRecap - Smart Pointer Mock3Advanced Enums
Enums with DataMethods on EnumsMatching Data VariantsThe Option Enum RevisitedRecap - Shape Enum