Functional programming with Options and Results
In this previous chapter, we've seen how to use functional programming with iterators. In this chapter we'll see how to
use functional programming with Option and Result.
Option
The Option type is used in Rust to represent a value that can be either something or nothing. The Option type is
defined as follows:
#![allow(unused)] fn main() { enum Option<T> { Some(T), None, } }
The Option type is generic, meaning that it can hold any type of value. The Some variant holds a value of type T,
and the None variant represents the absence of a value.
It can be very useful to use a map method on an Option to transform the value inside the Option. If the Option
contains a value, the map method applies the function to the value and returns a new Option containing the result.
If the Option is None, the map method does nothing and returns None.
Let's look at an example:
fn main() { let maybe_number = Some(5); let maybe_number_plus_one = maybe_number.map(|number| number + 1); println!("{maybe_number_plus_one:?}"); // Some(6) let maybe_number = None; let maybe_number_plus_one = maybe_number.map(|number| number + 1); println!("{maybe_number_plus_one:?}"); // None }
In a similar way you can also assign a default value to an Option using the unwrap_or method. If the Option
contains a value, the unwrap_or method returns the value. If the Option is None, the unwrap_or method returns
the default value.
fn main() { let maybe_number = Some(5); let number = maybe_number.unwrap_or(0); println!("{number:?}"); // 5 let maybe_number = None; let number = maybe_number.unwrap_or(0); println!("{number:?}"); // 0 }
If the map operation returns an Option and you want to flatten the result, you can use the and_then method. The
and_then method applies the function to the value inside the Option and returns the result. If the Option is
None, the and_then method does nothing and returns None.
fn main() { let only_even_numbers = |number| -> Option<i32> { if number % 2 == 0 { Some(number) } else { None } }; let maybe_even = Some(6).and_then(only_even_numbers); println!("{maybe_even:?}"); // Some(6) let maybe_odd = Some(5).and_then(only_even_numbers); println!("{maybe_odd:?}"); // None }
Results
The Result type is used in Rust to represent a value that can be either a success or a failure. The Result type is
defined as follows:
#![allow(unused)] fn main() { enum Result<T, E> { Ok(T), Err(E), } }
The Result type is also generic, meaning that it can hold any type of success value and any type of error value. The
Ok variant holds a success value of type T, and the Err variant holds an error value of type E.
In that respect, Result is similar to Option, but with the added ability to hold an error value.
With that in mind, you can see how Result can be used in a similar way to Option. You can use a map method on a
Result to transform the value inside the Result. If the Result contains a success value, the map method applies
the function to the value and returns a new Result containing the result. If the Result is an error, the map
method does nothing and returns the error.
fn main() { let maybe_number: Result<i32, &str> = Ok(5); let maybe_number_plus_one = maybe_number.map(|number| number + 1); println!("{maybe_number_plus_one:?}"); // Ok(6) let maybe_number: Result<i32, &str> = Err("error"); let maybe_number_plus_one = maybe_number.map(|number| number + 1); println!("{maybe_number_plus_one:?}"); // Err("error") }
Many of the operations that can be performed on Option can also be performed on Result. For example, you can use the
unwrap_or method to assign a default value to a Result. If the Result contains a success value, the unwrap_or
method returns the value. If the Result is an error, the unwrap_or method returns the default value.
Switching between Option and Result
You can use the ok method to convert an Option to a Result. If the Option contains a value, the ok method
returns a Result containing the value. If the Option is None, the ok method returns a Result containing the
default error value.
And vice versa, you can use the ok_or method to convert a Result to an Option. If the Result contains a success
value, the ok_or method returns an Option containing the value. If the Result is an error, the ok_or method
returns an Option containing the error value.
fn main() { let maybe_number = Some(5); let result = maybe_number.ok_or("error"); println!("{result:?}"); // Ok(5) let maybe_number: Option<i32> = None; let result = maybe_number.ok_or("error"); println!("{result:?}"); // Err("error") let result: Result<i32, &str> = Ok(5); let maybe_number = result.ok(); println!("{maybe_number:?}"); // Some(5) let result: Result<i32, &str> = Err("error"); let maybe_number = result.ok(); println!("{maybe_number:?}"); // None }