6.2.1. What Is the Option Enum?
It is defined in the standard library and included in the prelude (the pre-imported module). It is used to describe a scenario where:
a value may exist, and if so, what data type it has; or it may simply not exist.
6.2.2. Rust Has No Null
In most other languages, there is a Null value, which represents no value.
In those languages, a variable can be in two states:
- Null (
Null) - Non-null
Null’s inventor, Tony Hoare, said in his 2009 talk “Null References: The Billion Dollar Mistake”:
I call it my billion-dollar mistake. At that time, I was designing the first comprehensive type system for references in an object-oriented language. My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn’t resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.
The problem with Null is very obvious, and even its inventor does not think it is a good thing. For example, if a variable is of type string and needs to be concatenated with another string, but the variable is actually Null, then an error will occur during concatenation. For Java users, the most common error is NullPointerException. In one sentence, when you try to use a Null value as if it were a non-Null value, some kind of error will occur.
Therefore, Rust does not provide Null. However, for the concept that Null is trying to express—namely, a value that is currently invalid or does not exist for some reason—Rust provides a similar enum called Option<T>.
6.2.3. Option<T>
It is defined in the standard library like this:
enum Option<T>{
Some(T),
None,
}
- The
Somevariant can carry some data, and its data type isT.<T>is actually a generic parameter (covered later). -
Noneis the other variant, but it does not carry any data, because it represents the case where a value does not exist.
Because it is included in the Prelude, you can use Option<T>, Some(T), and None directly.
Look at an example:
fn main(){
let some_number = Some(5);
let some_char = Some('e');
let absent_number: Option<i32> = None;
}
- For the first two statements, the values are written inside the parentheses, so the Rust compiler can infer their data types. For example,
some_numberhas typeOption<i32>, andsome_charhas typeOption<char>. Of course, you can also write the type explicitly, but it is unnecessary unless you want to force a specific type. - For the last statement, the assigned value is the
Nonevariant. The compiler cannot infer fromNonewhat typeTinOption<T>should be, so you need to declare the concrete type explicitly. That is whyOption<i32>is written here.
In this example, the first two variables are valid values, while the last variable does not contain a valid value.
6.2.4. The Advantages of Option<T>
- In Rust,
Option<T>andT(Tcan be any data type) are different types. You cannot treatOption<T>asT. - If you want to use the
TinsideOption<T>, you must first convert it toT. This prevents programmers from ignoring the possibility of null values and directly operating on variables that may be empty. Rust’sOption<T>design forces developers to handle these cases explicitly. For example, in C#, if you writestring a = null;and thenstring b = a + "12345";, and you do not check whetherais null (or ignore the possibility thatais null), an error will occur. In Rust, as long as a value’s type is notOption<T>, that value is definitely not null.
For example:
fn main(){
let x: i8 = 5;
let y: Option<i8> = Some(5);
let sum = x + y;
}
If you run this code, the compiler will report an error:
error[E0277]: cannot add `Option<i8>` to `i8`
--> src/main.rs:5:17
|
5 | let sum = x + y;
| ^ no implementation for `i8 + Option<i8>`
|
= help: the trait `Add<Option<i8>>` is not implemented for `i8`
= help: the following other types implement trait `Add<Rhs>`:
`&i8` implements `Add<i8>`
`&i8` implements `Add`
`i8` implements `Add<&i8>`
`i8` implements `Add`
The error means that the types Option<i8> and i8 cannot be added together because they are not the same type.
So how can we make x and y add together? It is simple: convert y from Option<i8> to i8:
fn main() {
let x: i8 = 5;
let y: Option<i8> = Some(5);
let sum = match y {
Some(value) => x + value, // If y is Some, unwrap and add
None => x, // If y is None, return x
};
}