Jump to content
Main menu
Main menu
move to sidebar
hide
Navigation
Main page
Recent changes
Random page
Help about MediaWiki
Special pages
JOHNWICK
Search
Search
Appearance
Create account
Log in
Personal tools
Create account
Log in
Pages for logged out editors
learn more
Contributions
Talk
Editing
Option combinators
Page
Discussion
English
Read
Edit
View history
Tools
Tools
move to sidebar
hide
Actions
Read
Edit
View history
General
What links here
Related changes
Page information
Appearance
move to sidebar
hide
Warning:
You are not logged in. Your IP address will be publicly visible if you make any edits. If you
log in
or
create an account
, your edits will be attributed to your username, along with other benefits.
Anti-spam check. Do
not
fill this in!
Incase you missed yesterdayās writeup where we covered Modules. You can check it through the link below. Modules I missed publishing yesterdayās writeup but here we are. If you missed our discussion on smart pointers check it out⦠medium.com RustāsĀ OptionĀ type is crucial to its safety guarantees, letting us explicitly handle cases where a value might beĀ Some(T)Ā orĀ None. Earlier on we had seen the basics ofĀ Option, like how it replaces null pointers with a safer alternative. Now, weāll focus onĀ Option combinators which are essentiallyĀ methods that let you chain operations onĀ OptionĀ values to write cleaner, more expressive code. These methods are for transforming, combining, or extracting values fromĀ Options without drowning inĀ matchĀ statements orĀ if letĀ blocks. Weāll go through key combinators, show how they work, and tie it all together with examples that feel like tasks youād encounter in a codebase. Letās start with some of the most common combinators and see how they apply to scenarios you might face in a typical Rust project. map TheĀ mapĀ combinator is your go-to when you want to apply a function to the value inside aĀ Some, but do nothing if itāsĀ None. It takes anĀ Option<T>, applies a function to theĀ TĀ if it exists, and wraps the result in a newĀ Option. Formatting User Data Suppose youāre working on a platform where you need to display a userās full name on their profile page. The userās profile might have a middle name, but itās optional. You want to format the full name as āFirst Middle Lastā if the middle name exists, or āFirst Lastā if it doesnāt. Hereās how youād useĀ mapĀ to handle this: <pre> struct User { first_name: String, middle_name: Option<String>, last_name: String, } fn format_full_name(user: &User) -> String { user.middle_name .as_ref() .map(|middle| format!("{} {} {}", user.first_name, middle, user.last_name)) .unwrap_or(format!("{} {}", user.first_name, user.last_name)) } fn main() { let user1 = User { first_name: "Alice".to_string(), middle_name: Some("Marie".to_string()), last_name: "Smith".to_string(), }; let user2 = User { first_name: "Bob".to_string(), middle_name: None, last_name: "Jones".to_string(), }; println!("{}", format_full_name(&user1)); println!("{}", format_full_name(&user2)); } </pre> In our code above,Ā mapĀ applies the formatting function only ifĀ middle_nameĀ isĀ Some, and we useĀ unwrap_orĀ to provide a fallback for theĀ NoneĀ case. This is cleaner than aĀ matchĀ statement checking forĀ SomeĀ orĀ NoneĀ explicitly. In some project, this pattern is common when transforming optional data, like formatting addresses, phone numbers, or optional settings, without cluttering your code with conditionals. This example above showed howĀ mapĀ keeps your logic focused and readable. But what if you need to chain multiple operations where each one might produce a newĀ Option? Thatās whereĀ and_thenĀ is useful. and_then TheĀ and_thenĀ combinator is perfect when you need to perform an operation that itself returns anĀ Option. UnlikeĀ map, which wraps the result in anĀ Option,Ā and_thenĀ flattens the result, so you donāt end up with nestedĀ Option<Option<T>>. Picture working on a task management app. You need to fetch a taskās assignee, then check if they have an email address, and finally validate that email before sending a reminder. Each step depends on the previous one succeeding, and any step could returnĀ None. Hereās howĀ and_thenĀ helps: <pre> struct User { email: Option<String>, } struct Task { assignee: Option<User>, } fn validate_email(email: &str) -> Option<String> { if email.contains("@") { Some(email.to_string()) } else { None } } fn get_valid_assignee_email(task: &Task) -> Option<String> { task.assignee.as_ref().and_then(|user| user.email.as_ref().and_then(|email| validate_email(email))) } fn main() { let task1 = Task { assignee: Some(User { email: Some("alice@example.com".to_string()), }), }; let task2 = Task { assignee: Some(User { email: Some("invalid_email".to_string()), }), }; let task3 = Task { assignee: None }; println!("{:?}", get_valid_assignee_email(&task1)); println!("{:?}", get_valid_assignee_email(&task2)); println!("{:?}", get_valid_assignee_email(&task3)); } </pre> In code above,Ā and_thenĀ chains the operations. First checking if thereās an assignee, then checking if they have an email, and finally validating the email. If any step results inĀ None, the chain short-circuits, andĀ NoneĀ is returned. This is a pattern youāll see all over in APIs, database queries, or any system where you need to drill down through layers of optional data, like fetching a userās profile, their preferences, and then a specific setting. Now that weāve seen how to transform and chain operations, what if you just want to provide a default value or handle theĀ NoneĀ case gracefully. Letās look atĀ unwrap_orĀ and its relatives. unwrap_or,Ā unwrap_or_else, andĀ unwrap_or_default These combinators let you specify what happens when anĀ OptionĀ isĀ NoneĀ and are useful when you want to avoid explicitĀ matchĀ statements but still provide a fallback value. * unwrap_orĀ takes a static default value. * unwrap_or_elseĀ takes a closure to compute the default, which is only called if theĀ OptionĀ isĀ None. * unwrap_or_defaultĀ uses the typeās default value (if it implementsĀ Default). Below is an example how youād use these combinators. <pre> fn get_timeout(config: &Option<u32>) -> u32 { config.unwrap_or_else(|| { // Simulate fetching a default from environment or system if std::env::var("FAST_MODE").is_ok() { 30 } else { 60 } }) } fn main() { let config1 = Some(45); let config2 = None; println!("Timeout: {}", get_timeout(&config1)); // Output: Timeout: 45 println!("Timeout: {}", get_timeout(&config2)); // Output: Timeout: 60 (or 30 if FAST_MODE is set) } </pre> In the code above,Ā unwrap_or_elseĀ lets you compute the default lazilyāonly if theĀ OptionĀ isĀ None. This is great for cases where calculating the default is expensive, like querying a database or reading environment variables. In a real project, you might use this for settings like retry counts, log levels, or API keys, where defaults depend on runtime conditions. This pattern is powerful, but sometimes you need to filter values based on a condition. Thatās whereĀ filterĀ comes in handy. filter TheĀ filterĀ combinator lets you keep anĀ Optionās value only if it satisfies a condition. If the condition fails or theĀ OptionĀ isĀ None, you getĀ None. Imagine youāre working on a file-sharing service, and you need to check if a user has permission to access a file. The userās role is optional (e.g., they might not be logged in), and you only want to proceed if their role is āadminā. The code below is howĀ filterĀ fits in: <pre> struct File { name: String, } fn get_file_if_admin(role: Option<&str>, file: &File) -> Option<&File> { role.filter(|&r| r == "admin").map(|_| file) } fn main() { let file = File { name: "secret.txt".to_string(), }; let role1 = Some("admin"); let role2 = Some("guest"); let role3 = None; println!("{:?}", get_file_if_admin(role1, &file).map(|f| &f.name)); println!("{:?}", get_file_if_admin(role2, &file).map(|f| &f.name)); println!("{:?}", get_file_if_admin(role3, &file).map(|f| &f.name)); } </pre> In our code snippet above,Ā filterĀ ensures that only users with the āadminā role allow the function to return the file. Often this is a common pattern in access control systems, where you need to gate access based on optional user attributes like roles, permissions, or subscription status. Here are a few practical tips to keep note of when working with option combinators we have discussed above. * UseĀ mapĀ for simple transformations,Ā and_thenĀ for operations that returnĀ Option, andĀ unwrap_or_elseĀ for computed defaults. * Break long chains into smaller functions or add comments to clarify each step. * Combinators likeĀ mapĀ andĀ and_thenĀ also work withĀ Result, so you can use similar patterns for error handling. * Always test edge cases to ensure your fallbacks work as expected. Read the full article here: https://medium.com/rustaceans/option-combinators-7988f0b6c7c7
Summary:
Please note that all contributions to JOHNWICK may be edited, altered, or removed by other contributors. If you do not want your writing to be edited mercilessly, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource (see
JOHNWICK:Copyrights
for details).
Do not submit copyrighted work without permission!
Cancel
Editing help
(opens in new window)
Search
Search
Editing
Option combinators
Add topic