You don't want null
. null
is an unsafe antipattern even in languages where you have to use it, and thankfully Rust rids us of the atrocity. Box<T>
always contains a T
, never null
. Rust has no concept of null
.
As you've correctly pointed out, if you want a value to be optional, you use Option<T>
. Whether you do Box<Option<T>>
or Option<Box<T>>
really doesn't matter that much, and someone who knows a bit more about the lower-level side of things can chime in on which is more efficient.
struct Node {
value: i32,
next: Option<Box<Node>>,
}
struct Stack {
top: Option<Box<Node>>,
}
The Option
says "this may or may not exist" and the Box
says "this value is on the heap. Now, the nice thing about Option
that makes it infinitely better than null
is that you have to check it. You can't forget or the compiler will complain. The typical way to do so is with match
match my_stack.top {
None => {
// Top of stack is not present
}
Some(x) => {
// Top of stack exists, and its value is x of type Box<T>
}
}
There are tons of helper methods on the Option
type itself to deal with common patterns. Below are just a few of the most common ones I use. Note that all of these can be implemented in terms of match
and are just convenience functions.
The equivalent of the following Java code
if (value == null) {
result = null;
} else {
result = ...;
}
is
let result = value.map(|v| ...)
Or, if the inner computation can feasibly produce None
as well,
let result = value.and_then(|v| ...)
If you want to provide a default value, say zero, like
if (value == null) {
result = 0;
} else {
result = value;
}
Then you want
result = value.unwrap_or(0)
It's probably best to stop thinking in terms of how you would handle null
and start learning Option<T>
from scratch. Once you get the hang of it, it'll feel ten times safer and more ergonomic than null
checks.