Option and Enum
This lesson will teach about a built-in enum called option.
What Is Option?
Option is a built-in enum in the Rust standard library. It has two variants Some and None.
Variants:
Some(T), returns Some value TNone, returns no value
When to Use Option?
Options is a good choice when:
-
The return value is none
Rust avoids including nulls in the language, unlike other languages. For instance, the function that returns a value may actually return nothing. So, here the Option variant
Nonecomes in handy. -
The value of the variable is optional
The value of any variable can be set to some value or set to none.
-
Out of bound exception is to be displayed
This is useful in the case of an array, string or a vector when an invalid index number tries to access it.
Example 1: Return Value Is None
The following example shows that if the else construct has no value then it can simply return None.
fn main() {println!("{:?}", learn_lang("Rust"));println!("{:?}", learn_lang("Python"));}fn learn_lang(my_lang:&str)-> Option<bool> {if my_lang == "Rust" {Some(true)} else {None}}
Explanation
-
From line 1 to 4,
mainfunction is defined.- On line 2, the function
learn_langis invoked by passing “Rust” within the function parameter. - On line 3, the function
learn_langis invoked by passing “Python” within the function parameter.
- On line 2, the function
-
From line 5 to 11,
learn_langfunction is defined. The functionlearn_langtakes a parametermy_langand return anOption<bool>.-
On line 6, an
ifcondition checks if the value ofmy_langis equal toRustthen it returnsSome(true). Note that the return type ofOptionis bool so true is passed within theSome. -
On line 7,
elseis executed if theifcondition evaluates to be false and returnsNone.
Note:
Nonedoes not take a parameter unlikeSome. -
Example 2: Optional Variable Value
The following example makes level variable of the struct Course as Option of type String. That means that it’s optional to set any value to it. It can be set to some value or it can be set to none.
//declare a structstruct Course {code:i32,name:String,level: Option<String>,}fn main() {//initializelet course1 = Course {name:String::from("Rust"),level:Some(String::from("beginner")),code:130};let course2 = Course {name:String::from("Javascript"),level:None,code:122};//accessprintln!("Name:{}, Level:{} ,code: {}", course1.name, course1.level.unwrap_or("Level".to_string()), course1.code);println!("Name:{}, Level:{} ,code: {}", course2.name, course2.level.unwrap_or("No level defined!".to_string()), course2.code);}
Explanation
- Line 2-5, a
structCoursehas three itemscode,name,levelof typei32,String, andOption<String>respectively. - From line 7 to line 22,
mainfunction is defined.- From line 9 to line 13, a variable
course1instantiates theCourse. On line 11, it initializes theleveltoSomevalue. Here the value is to set to a String object, i.e., “beginner”. Note the Option has a typeStringso a value of type String can only be set to it. - From line 14 to line 17, a variable
course2instantiates theCourse. On line 16, it initializes theleveltoNonevalue. - On line 20, the items of struct instance
course1is printed using the member access operator (.). To print thelevelitem of thecourse1instance.unwrap_or()built-in method is used because thelevelis of typeOption, its values are accessed using.unwrap_or()with a string parameter passed to it. In this case, since the level is initialized toSome(String::from("beginner")), it prints the level. But if it is set to None, it prints the string within the.unwrap_or()method. - On line 21, the items of struct instance
course2is printed using the member access operator (.). To print thelevelitem of thecourse2instance.unwrap_or()built-in method is used because thelevelis of typeOption, and it is initialized with the valueNone.
- From line 9 to line 13, a variable
Example 3: Index Out of Bound Exception
The example below uses a match statement that takes an index of string
using match.str.chars().nth(index_no)
and executes the Some block if index_no is in range and None block otherwise.
fn main() {// define a variablelet str = String :: from("Educative");// define the index value to be foundlet index = 12;lookup(str, index);}fn lookup(str: String, index: usize) {let matched_index = match str.chars().nth(index){// execute if match found print the value at specified indexSome(c)=>c.to_string(),// execute if value not foundNone=>"No character at given index".to_string()};println!("{}", matched_index);}
Explanation
-
From line 1 to 7,
mainfunction is defined.- On line 3, a variable
stris initialized with valueEducativeof typeString. - On line 5, a variable
indexis initialized with the value12. - On line 6, function
lookupis invoked which takesstrandindexas parameters to the function.
- On line 3, a variable
-
From line 8 to line16, function
loopupis defined.- On line 9, a variable
matched_indexsaves the value ofmatchstatement that takesstr.chars().nth(index_no)as a condition. Herestrandindexare the values passed as an argument to the function..chars().nth(index)finds the character at givenindex. - On line 11,
Some(c)checks if the character is found at the given index, then it returns the character. Else, - On line 13,
Nonereturns a string saying that the character is not found.
- On line 9, a variable
is_some(), is_none() Functions
Rust provides is_some() and is_none() to identify the return type of variable of type Option, i.e., whether the value of type Option is set to Some or None.
Example 1
The following example checks whether the variable value of type Option is set to Some or None.
fn main() {let my_val: Option<&str> = Some("Rust Programming!");print(my_val); // invoke the function}fn print(my_val: Option<&str>){if my_val.is_some(){ // check if the value is equal to some valueprintln!("my_val is equal to some value");}else{println!("my_val is equal to none");}}
Explanation
-
The
mainfunction is defined from line 1 to line 5.- On line 2, a variable
my_varis declared and its value is set toSome("Rust Programming"). - On line 3, a function
printis invoked which takes the variablemy_varas an argument to the function.
- On line 2, a variable
-
On line 6-13, function
printis defined.- The
ifconstruct checks if the variablemy_varis initialized to some value using the built-in method.is_some(). If it is, it prints that the variable is set to some value. - Else, it prints it is set to none.
- The
We need to do is to ensure that these functions return true or false. That’s where assert_eq and assert_ne functions come in handy.
Assert Macros
assert_eq!(left, right)- evaluates to true if left value is equal to that of rightassert_ne!(left, right)- evaluates to true if left value is not equal to that of right
Output of
assertexpression?If the assertion passes no output is displayed, and if doesn’t the code gives an error saying that the assertion failed.
Example 2
The following example uses the assert_eq! macro to check whether the variable value of type Option is set to Some or None.
Note: The assertion passes since the expression evaluates to true.
fn main() {let my_val: Option<&str> = Some("Rust Programming!");// pass since my_val is set to some value so left is true, and right is also trueassert_eq!(my_val.is_some(), true);// pass since my_val is set to some value so left is false, and right is also falseassert_eq!(my_val.is_none(), false);}
Explanation
- On line 2, declares a variable
my_varand sets it toSome("Rust Programming"). - On line 4, an
assert_eq!takesvar.is_some()and checks if it’s equal totrue. Sincemy_valis set to some value so leftmy_val.is_some()is true and right is also set totrue. The assertion passes. - On line 6, an
assert_eq!takes the expressionvar.is_none()and checks if it’s equal tofalse.Sincemy_valis set to some value so leftmy_val.is_none()is false and right is also set tofalse. So the assertion passes.
There is often a situation when you have to display messages like “File Not found” and “Ok” on failures and successes respectively. Let’s see how the built-in Result enum can help you do this in the next lesson.