Options
Type Option represents an optional value: every Option is either Some and contains a value, or None, and does not.
Option types are very common in Rust code, as they have a number of uses:
- Initial values
- Return values for functions that are not defined over their entire input range (partial functions)
- Return value for otherwise reporting simple errors, where None is returned on error
- Optional struct fields
- Struct fields that can be loaned or “taken”
- Optional function arguments
- Nullable pointers
- Swapping things out of difficult situations
Further Information
- ✨Generic Data Types->Option Enum Format - The Rust Programming Language
- Option Module Documentation
- Option Enum Documentation
- if let - The Rust Programming Language
- while let - The Rust Programming Language
rustlings-solutions-5/options at main · gaveen/rustlings-solutions-5
Rustlings
options1
// options1.rs // Execute `rustlings hint options1` or use the `hint` watch subcommand for a hint. // I AM NOT DONE // This function returns how much icecream there is left in the fridge. // If it's before 10PM, there's 5 pieces left. At 10PM, someone eats them // all, so there'll be no more left :( fn maybe_icecream(time_of_day: u16) -> Option<u16> { // We use the 24-hour system here, so 10PM is a value of 22 and 12AM is a value of 0 // The Option output should gracefully handle cases where time_of_day > 23. // TODO: Complete the function body - remember to return an Option! ? ? ? } // convert unit tests to main fn main() { fn check_icecream() { assert_eq!(maybe_icecream(9), Some(5)); assert_eq!(maybe_icecream(10), Some(5)); assert_eq!(maybe_icecream(23), Some(0)); assert_eq!(maybe_icecream(22), Some(0)); assert_eq!(maybe_icecream(25), None); } fn raw_value() { // TODO: Fix this test. How do you get at the value contained in the Option? let icecreams = maybe_icecream(12); assert_eq!(icecreams, 5); } check_icecream(); raw_value(); } #[cfg(test)] mod tests { use super::*; #[test] fn check_icecream() { assert_eq!(maybe_icecream(9), Some(5)); assert_eq!(maybe_icecream(10), Some(5)); assert_eq!(maybe_icecream(23), Some(0)); assert_eq!(maybe_icecream(22), Some(0)); assert_eq!(maybe_icecream(25), None); } #[test] fn raw_value() { // TODO: Fix this test. How do you get at the value contained in the Option? let icecreams = maybe_icecream(12); assert_eq!(icecreams, 5); } }
Hint
Options can have a Some value, with an inner value, or a None value, without an inner value.
There’s multiple ways to get at the inner value, you can use unwrap, or pattern match.
Unwrapping is the easiest, but how do you do it safely so that it doesn’t panic in your face later?
solution1: pattern match
fn maybe_icecream(time_of_day: u16) -> Option<u16> { // We use the 24-hour system here, so 10PM is a value of 22 // The Option output should gracefully handle cases where time_of_day > 24. match time_of_day { 0..=21 => Some(5), 22..=24 => Some(0), _ => None, } // Exclusive range (e.g., 0..22) pattern use here is experimental // on rustc 1.62.1 }
assert_eq!(icecreams.unwrap_or(0), 5); // Use unwrapped Some or 0
options2
// options2.rs // Execute `rustlings hint options2` or use the `hint` watch subcommand for a hint. // I AM NOT DONE // convert unit tests to main fn main() { fn simple_option() { let target = "rustlings"; let optional_target = Some(target); // TODO: Make this an if let statement whose value is "Some" type word = optional_target { assert_eq!(word, target); } } fn layered_option() { let mut range = 10; let mut optional_integers: Vec<Option<i8>> = Vec::new(); for i in 0..(range + 1) { optional_integers.push(Some(i)); } // TODO: make this a while let statement - remember that vector.pop also adds another layer of Option<T> // You can stack `Option<T>`'s into while let and if let integer = optional_integers.pop() { assert_eq!(integer, range); range -= 1; } } simple_option(); layered_option(); } #[cfg(test)] mod tests { #[test] fn simple_option() { let target = "rustlings"; let optional_target = Some(target); // TODO: Make this an if let statement whose value is "Some" type word = optional_target { assert_eq!(word, target); } } #[test] fn layered_option() { let mut range = 10; let mut optional_integers: Vec<Option<i8>> = Vec::new(); for i in 0..(range + 1) { optional_integers.push(Some(i)); } // TODO: make this a while let statement - remember that vector.pop also adds another layer of Option<T> // You can stack `Option<T>`'s into while let and if let integer = optional_integers.pop() { assert_eq!(integer, range); range -= 1; } } }
Hint
check out:
Remember that Options can be stacked in if let and while let. For example: Some(Some(variable)) = variable2 Also see Option::flatten
solution: if let & while let
if let Some(word) = optional_target { assert_eq!(word, target); }
while let Some(integer) = optional_integers.pop().flatten() { assert_eq!(integer, range); range -= 1; }
options3: ref -> value partially moved here
// options3.rs // Execute `rustlings hint options3` or use the `hint` watch subcommand for a hint. // I AM NOT DONE struct Point { x: i32, y: i32, } fn main() { let y: Option<Point> = Some(Point { x: 100, y: 200 }); match y { Some(p) => println!("Co-ordinates are {},{} ", p.x, p.y), _ => println!("no match"), } y; // Fix without deleting this line. }
Hint
The compiler says a partial move happened in the match
statement.
How can this be avoided? The compiler shows the correction needed. After making the correction as suggested by the compiler, do read: ref - Rust