<span>Option<T></span>
How do you handle null objects in programming development?
As a Java programmer, before JDK8, you might have encountered code like this:
if (obj != null) {
//TODO
}
During project execution, you often encounter <span>NullPointerException</span>, which can be quite painful.
However, if you program in Rust, you don’t have to worry about these exceptions. Rust does two things to handle this:
1. All variables in Rust must be initialized before use, so there are no undefined values.
2. Rust defines null values as <span>Option<T>::None</span>, and establishes this standard at the library level, which applications should follow during design.
1. Definition of Option
pub enum Option<T> {
None,
Some(T),
}

This is a generic enumeration, where <span>T</span> indicates it can contain any type of data.<span>Option<T></span> can have two variants:
<span>Some(T)</span>: indicates there is a value of type<span>T</span>.<span>None</span>: indicates there is no value.
By using <span>Option<T></span>, Rust forces programmers to handle potential null values at compile time, greatly increasing the reliability and robustness of the code.
2. Handling Option
2.1 is_some and is_none
Used to determine if there is a value inside an Option.
fn main() {
let number = Some(5);
if number.is_some(){
println!("number is not null");
}
if number.is_none(){
println!("number is null");
}
}
2.2 match
Option has two cases, Some and None. If you want to handle both cases, you can use match:
fn main() {
match_option(Some(10));
match_option(None);
}
fn match_option(value: Option<i32>){
match value {
Some(t) => println!("The value is: {}",t),
None => println!("No value")
}
}
2.3 if let
The above match handles both cases. If you only want to handle the case where Some has a value, you can use if let.
fn main() {
if_let_option(Some(10));
}
fn if_let_option(value: Option<i32>){
if let Some(t) = value{
println!("The value is: {}",t);
}
}
2.4 unwrap
<span>unwrap</span> extracts the value from <span>Option<T></span>, but if it is <span>None</span>, it will panic (terminate the program).
fn main() {
let value = Some(30);
let number = value.unwrap(); // Note: this will panic if it is None
println!("Extracted value: {}", number);
}
fn main() {
let value = None;
let number = value.unwrap(); // Note: this is None, will panic
println!("Extracted value: {}", number);
}

2.5 expect
<span>expect</span> is similar to <span>unwrap</span>, it will panic when encountering None. However, it allows us to specify an error message, which is helpful for debugging.
fn main() {
let value = None;
let number = value.expect("Value is None"); // Note: this is None, will panic
println!("Extracted value: {}", number);
}

2.6 unwrap_or
<span>unwrap_or</span> allows us to specify a default value, which will be returned when <span>Option</span> is <span>None</span>, instead of panicking.
fn main() {
let value = None;
let number = value.unwrap_or(50); // If it is None, return default value 50
println!("Extracted value or default value: {}", number);
}
2.7 unwrap_or_else
<span>unwrap_or_else</span> is similar to <span>unwrap_or</span>, but it allows us to provide a function that returns the default value.
fn main() {
let option = Some(10);
let value = option.unwrap_or_else(default_value);
println!("Value is: {}", value); // Will print: Value is: 10
let option_none: Option<i32> = None;
let value = option_none.unwrap_or_else(default_value);
println!("Value is: {}", value); // Will print: Value is: 42
}
fn default_value() -> i32 {
// This can contain more complex logic
42
}
2.8 unwrap_or_default
<span>unwrap_or_default</span> extracts the value from Option, and if it is None, it will not panic, but return the default value of type T.
For example, if T is i32, its default value is 0.
fn main() {
let x: Option<i32> = None;
let x = x.unwrap_or_default();
assert_eq!(0, x);
}
2.9 map
<span>map</span> method applies a function to the value inside <span>Option<T></span>. If it is <span>Some(T)</span>, it returns <span>Some(U)</span>, otherwise it returns <span>None</span> without doing anything.
fn main() {
let value = Some(5);
let result = value.map(|x| x * 2); // Multiply the value by 2
println!("{:?}", result); // Output Some(10)
}
2.10 and_then
<span>and_then</span> method can be used to chain multiple Option generating operations, and it only executes if the previous Option is Some.
fn maybe_double(number: Option<i32>) -> Option<i32> {
number.and_then(|n| Some(n * 2))
}
fn main() {
let number = Some(5);
let result = maybe_double(number);
println!("{:?}", result); // Output Some(10)
}
2.11 cloned
Clones the content inside Option, converting <span>Option<&T></span> into <span>Option<T></span>.
Method source code:
pub fn cloned(self) -> Option<T>
where
T: Clone,
{
match self {
Some(t) => Some(t.clone()),
None => None,
}
}
Test case:
let x = 12;
let opt_x = Some(&x);
assert_eq!(opt_x, Some(&12));
let cloned = opt_x.cloned();
assert_eq!(cloned, Some(12));
2.12 as_ref
Converts <span>Option<T></span> or <span>&Option<T></span><code><span> into </span><code><span>Option<&T></span>.
Creates a new Option, where the type is a reference to the original type, converting from <span>Option<T></span> to <span>Option<&T></span>. The original <span>Option<T></span> instance remains unchanged.
let x = 12;
let opt_x = Some(x);
let opt_x_ref = opt_x.as_ref();
assert_eq!(Some(&12), opt_x_ref);
assert_eq!(Some(12), opt_x);
2.13 as_mut
Converts <span>Option<T></span> or <span>&mut Option<T></span> into <span>Option<&mut T></span>.
fn main() {
let mut x = Some(2);
match x.as_mut() {
Some(v) => *v = 4,
None => {},
}
assert_eq!(x, Some(4));
}
2.14 take
Takes the value from Option, leaving a None value in its place.
This is very useful, as it allows you to take the value out without consuming the original Option.
let mut x = Some(2);
let y = x.take();
assert_eq!(x, None);
assert_eq!(y, Some(2));
let mut x: Option<u32> = None;
let y = x.take();
assert_eq!(x, None);
assert_eq!(y, None);
2.15 replace
Replaces the old value in place with a new value, while returning the old value.
let mut x = Some(2);
let old = x.replace(5);
assert_eq!(x, Some(5));
assert_eq!(old, Some(2));
let mut x = None;
let old = x.replace(3);
assert_eq!(x, Some(3));
assert_eq!(old, None);
<span>Result<T,E></span>
The <span>Result</span> type in Rust is an enumeration used for error handling, which is very suitable for operations that may fail.<span>Result</span> can safely express the result of successful or failed operations and forces developers to handle potential errors.
1. Definition of Result
pub enum Result<T, E> {
Ok(T),
Err(E),
}

<span>Result<T, E></span> is defined as an enumeration containing two variants, each carrying a type parameter as its payload. <span>Ok(T)</span> indicates a successful result, while <span>Err(E)</span> indicates an error.
In contrast to other languages’ conventions for function error returns, where C and Java sometimes use a return value of 0 to indicate success, and sometimes not, requiring you to determine what value represents success or error based on the context, Rust is different. A function’s return value, whether it is a success or an error, can be uniformly expressed using <span>Result<T, E></span>, making it more compact. Additionally, since <span>Result<T, E></span> is a type, T and E can be defined by us, making it very convenient to use.
For example:
let r: Result<String, String> = function();
This example indicates that the function’s return value is assigned to variable r, with a return type of <span>Result<String, String></span>. In the case of success, the return content is of type String; in the case of an error, the returned error type is also String.
2. Handling Result
2.1 is_ok and is_err
is_ok(): returns true if the Result is Ok.
is_err(): returns true if the Result is Err.
fn main() {
let res_ok: Result<i32, &str> = Ok(3);
let res_err: Result<i32, &str> = Err("some error message");
assert_eq!(res_ok.is_ok(), true); //true
assert_eq!(res_ok.is_err(), false); //true
assert_eq!(res_err.is_ok(), false); //true
assert_eq!(res_err.is_err(), true); //true
}
2.2 match
Similar to handling Option, match both cases:
match open_file("hello.txt") {
Ok(file) => println!("Successfully processed file"),
Err(e) => println!("Processing error: {}", e),
}
2.3 unwrap
<span>unwrap</span> extracts the value on success, and panics on failure.
fn main() {
let x: Result<u32, &str> = Ok(2);
assert_eq!(2, x.unwrap())
}
On failure:
fn main() {
let x: Result<i32, &str> = Err("some error message");
assert_eq!(2, x.unwrap())
}

2.4 expect
<span>expect</span> is similar to <span>unwrap</span>, it will panic when encountering Err, but allows us to specify a prompt message for debugging.
fn main() {
let x: Result<i32, &str> = Err("some error message");
assert_eq!(2, x.expect("Data error"))
}

2.5 unwrap_or
<span>unwrap_or</span> allows us to specify a default value, which will be returned when <span>Result</span> is <span>Err</span>, instead of panicking.
fn main() {
let x: Result<i32, &str> = Err("some error message");
assert_eq!(2, x.unwrap_or(2));
}
2.6 unwrap_or_else
<span>unwrap_or_else</span> is similar to <span>unwrap_or</span>, but it allows us to provide a function that returns the default value.
fn main() {
assert_eq!(Ok(2).unwrap_or_else(count), 2);
assert_eq!(Err("foo").unwrap_or_else(count), 3);
}
fn count(x: &str) -> usize {
x.len()
}
fn main() {
let x: Result<i32, &str> = Err("some error message");
assert_eq!(2, x.unwrap_or_else(|err| default_value()));
}
fn default_value() -> i32 {
// This can contain more complex logic
2
}
2.7 unwrap_or_default
<span>unwrap_or_default</span> extracts the value from Result<T,E>, and if it is Err, it will not panic, but return the default value of type T.
For example, if T is i32, its default value is 0.
fn main() {
let x: Result<i32, &str> = Err("some error message");
let x = x.unwrap_or_default();
assert_eq!(0, x);
}
2.8 map
<span>map</span> method applies a function to the T value inside <span>Result<T,E></span>. If it is <span>Err</span>, it does nothing.
fn main() {
let x: Result<i32, &str> = Ok(3);
let x = x.map(|x| x * 2);
println!("{:?}", x); // Ok(6)
}
2.9 and_then
<span>and_then</span> method can be used to chain multiple Result generating operations, and it only executes if the previous Result is Ok.
fn main() {
let x: Result<i32, &str> = Ok(3);
let x = x.and_then(|x| Ok(x * 2)).and_then(|x| Ok(x + 2));
println!("{:?}", x); // Ok(8)
}
2.10 as_ref
Creates a new Result, where the two types are references to the original types, converting from <span>Result<T, E></span> to <span>Result<&T, &E></span>. The original <span>Result<T, E></span> instance remains unchanged.
let x: Result<u32, &str> = Ok(2);
assert_eq!(x.as_ref(), Ok(&2));
let x: Result<u32, &str> = Err("Error");
assert_eq!(x.as_ref(), Err(&"Error"));
2.11 as_mut
Creates a new Result, where the two types are mutable references to the original types, converting from <span>Result<T, E></span> to <span>Result<&mut T, &mut E></span>. The original <span>Result<T, E></span> instance remains unchanged.
fn main() {
fn mutate(r: &mut Result<i32, i32>) {
match r.as_mut() {
Ok(v) => *v = 42,
Err(e) => *e = 0,
}
}
let mut x: Result<i32, i32> = Ok(2);
mutate(&mut x);
assert_eq!(x.unwrap(), 42);
let mut x: Result<i32, i32> = Err(13);
mutate(&mut x);
assert_eq!(x.unwrap_err(), 0);
}
2.12 map_err
When Result is Ok, it returns as is. When Result is Err, it applies the provided function or closure to the content carried by Err for computation and type conversion. This method is often used to convert the type of the payload carried by Result’s Err, widely used in error handling processes.
fn stringify(x: u32) -> String { format!("error code: {x}") }
let x: Result<u32, u32> = Ok(2);
assert_eq!(x.map_err(stringify), Ok(2));
let x: Result<u32, u32> = Err(13);
assert_eq!(x.map_err(stringify), Err("error code: 13".to_string()));
<span>Option<T></span> and <span>Result<T, E></span> Conversion
1. option.ok_or()
<span>Option<T></span> instance, if it is <span>Some</span>, directly wraps the content in <span>Result<T, E>::Ok()</span>. If it is <span>None</span>, it uses the parameter provided in <span>ok_or()</span> as the content of <span>Err</span>.
fn main() {
let x = Some("foo");
assert_eq!(x.ok_or(0), Ok("foo"));
let x: Option<&str> = None;
assert_eq!(x.ok_or(0), Err(0));
}
2. result.ok()
If <span>Result<T, E></span> is <span>Ok</span>, it wraps the content in <span>Some</span>. If <span>Result<T, E></span> is <span>Err</span>, it converts to <span>None</span>, discarding the content in <span>Err</span>, while the original <span>Result<T, E></span> instance is consumed.
let x: Result<u32, &str> = Ok(2);
assert_eq!(x.ok(), Some(2));
let x: Result<u32, &str> = Err("Nothing here");
assert_eq!(x.ok(), None);
For example, if String does not implement the <span>Copy</span> trait, calling the ok() method will consume the original <span>Result</span>, making it unavailable.
fn main() {
let my_result: Result<String, String> = Ok("abc".to_string());
let my_option = my_result.ok(); // my_result is consumed
// Attempting to use my_result again will cause a compilation error
println!("{:?}", my_result); // Compilation error: borrowed value has already been moved
}
3. result.err()
If <span>Result<T, E></span> is <span>Ok</span>, it converts to <span>None</span>, discarding the content in <span>Ok</span>. If <span>Result<T, E></span> is <span>Err</span>, it wraps the content in <span>Some</span>, while the original <span>Result<T, E></span> instance is consumed.
fn main() {
let x: Result<u32, &str> = Ok(2);
assert_eq!(x.err(), None);
let x: Result<u32, &str> = Err("Nothing here");
assert_eq!(x.err(), Some("Nothing here"));
}