How to use Options in RUST

Unlike in other languages where there is a null or None type to represent no data. In Rust we can use Option enum when there is a possibility of having a value or having no value at all arises. It is most commonly used to handle cases where a value might be missing or to explicitly handle potential null values.

It serves as a safer alternative to using null pointers or undefined values, common pitfalls in many other programming languages.

The Option  enum has two variants:

  1. Some(T) : Represents the value of type T
  2. None : Represents the absence of value

RUST Option Example

				
					fn divide(a: f64, b: f64) -> Option<f64> {
    if b != 0.0 {
        Some(a / b)
    } else {
        None
    }
}
fn main() {
    let result = divide(10.0, 5.0);
    match result {
        Some(value) => println!("Result: {}", value),
        None => println!("Cannot divide by zero"),
    }
}
				
			

We will understand little better with another use case. Suppose, we have a student management system and we request to get the grade of a particular student.

We define our struct first

 

				
					struct StudentRecord {
    name: String,
    grade: Option<u8>,
}
				
			

Imagine we are fetching the grade value from a datastore. So there are chance of presence or absence of the grade value. We use Option in this case. Now let’s write the function which fetches the students grade. 

				
					fn get_grade(studentname: &String, studentrecords: &Vec<StudentRecord>) -> Option<u8> {
    // Compare the vector of sturcts with the passed student name
    // Return the student grade else return None
    for student in studentrecords {
        if student.name == *studentname {
            return student.grade;
        }
    }
    None
}

				
			

As you can see the return type is an Option enum.

Now we define a vector which holds the students struct with each students information.

 

				
					fn main() {
        let students_list: Vec<StudentRecord> = vec![
            StudentRecord {
                name: String::from("John"),
                grade: Some(8),
            },
            StudentRecord {
                name: String::from("Bob"),
                grade: Some(9),
            },
            StudentRecord {
                name: String::from("Alice"),
                grade: None,
            },
        ];
				
			

You can see For the Student “Alice” we do not have the data in our datastore.  Lets, expand the main function to get the interested student grade.

				
					    fn main() {
        let students_list: Vec<StudentRecord> = vec![
            StudentRecord {
                name: String::from("John"),
                grade: Some(8),
            },
            StudentRecord {
                name: String::from("Bob"),
                grade: Some(9),
            },
            StudentRecord {
                name: String::from("Alice"),
                grade: None,
            },
        ];
        let check_name = String::from("Bob");
        let chosen_student_grade: Option<u8> = get_grade(&check_name, &students_list);
        match chosen_student_grade {
            Some(grade) => println!("The score is {grade}"),
            None => println!("Passed a null value")
        }
    }
				
			

Here you can see how due to Option Enum in the get_grade function, how the developer is forced to handle value missing cases. Thus reducing any run time errors.

Why is Option in RUST Beneficial for Developers?

1. Null Safety

One of the primary advantages of using the Option enum is to avoid null pointer errors, which are a common source of bugs and vulnerabilities in many programming languages. By explicitly handling the absence of values, Rust ensures that developers are more aware of potential null-related issues.

2. Expressive Error Handling

Option provides a clear and expressive way to handle optional values. Instead of relying on conventions or external documentation, the type system itself conveys whether a value is optional or mandatory.

3. Pattern Matching

Rust’s powerful pattern matching capabilities can be leveraged with the Option enum to handle both cases (Some and None) explicitly, leading to more robust and predictable code.

Use Cases for Option

1. Optional Function Parameters

When a function has parameters that are not always necessary, using Option can make the interface cleaner and more flexible.

				
					fn greet(name: Option<&#038;str>) {
    match name {
        Some(n) => println!("Hello, {}!", n),
        None => println!("Hello, world!"),
    }
}

				
			

2. Querying Databases

When querying databases, some fields may or may not have values. Using Option can represent this uncertainty effectively.

3. File Operations

File operations like reading or writing may or may not be successful. Option can be used to handle such scenarios.

Difference between Option and Result in RUST

While both Option and Result are enums that represent the absence or presence of a value, they serve different purposes:

  • Option: Represents the presence (Some) or absence (None) of a value.

  • Result: Represents the success (Ok) or failure (Err) of an operation, often accompanied by an error value.

In essence, Option is used for optional values, whereas Result is used for operations that can succeed or fail.

Share

Facebook
Twitter
Pinterest
LinkedIn