16  Some() or None?

Objective

Understand how missingness is handled in Rust with Option<T>. - Understand how to handle missingness - Use Option<T> in an applied case

Rust doesn’t have the concept of a NULL type. Null values are quite dangerous and have their own category of errors and security issues.

Rust makes it impossible to have a null value by formalizing the concept of missingness through the Option<>

Option

An Option is a special enum that has two possible values:

enum Option {
    Some(T),
    None
}

When a value is missing, the option’s variant is None. When the option contains some value, the value is contained inside of the Some(T) variant.

Accessing Some() value

Since Option<T> are “just” an enum, we can match on the variants to access the values.

// create an Option<Measure> that contains a value
let measure = Some(Measure::Euclidean);

match measure {
    Some(v) => println!("The measure is not missing!"),
    None => println!("Oh no! The measure is missing"),
}

We put a placeholder value inside of Some(v) to create a binding to the inner value. It’s like a function argument, it can be called whatever you want it to be.

Danger! .unwrap() & .expect()

Sometimes dealing with options is a headache, particularly when we’re in the early stages of developing.

We can use .unwrap() or .expect() to grab the inner value of an option without matching.

This is dangerous!! because we are ignoring the possibility of a None. Unwrapping on a None leads to a panic. Panics cause your program to abort.

thread 'main' panicked at src/main.rs:4:41:
called `Option::unwrap()` on a `None` value

Exercise

  • Modify the distance() method to take an Option<&Measure>
  • When None use euclidean distance, otherwise use the provided distance measure

The exercise does not involve .unwrap() because it’s a bad habbit. Let’s try and form good habbits from the outset if possible.

View solution
impl Point {
    fn distance(&self, destination: &Self, measure: Option<&Measure>) -> f64 {
        match measure {
            Some(m) => match m {
                Measure::Euclidean => self.euclidean_distance(destination),
                Measure::Haversine => self.haversine_distance(destination),
            },
            None => self.euclidean_distance(destination),
        }
    }
}