Error Handling
Some and None
Some(T) and None are the two variants of Option<T>. Because both variants are exported in the standard prelude, you construct and match Some(value) and None directly without writing Option::Some or Option::None.
Type Inference for Some(T) and None
Some(value)wraps a value of typeT, allowing the compiler to inferOption<T>directly fromvalue.Nonecarries no value, so the compiler must inferTfrom a variable annotation, a function signature, or a later use in the same scope.
Binding None without any type constraint is a compile-time error (E0282):
// ❌ does not compile
fn main() {
let unknown = None;
println!("{unknown:?}");
}
error[E0282]: type annotations needed for `Option<_>`
--> main.rs:2:9
|
2 | let unknown = None;
| ^^^^^^^ ---- type must be known at this point
|
help: consider giving `unknown` an explicit type, where the type for type parameter `T` is specified
|
2 | let unknown: Option<T> = None;
| +++++++++++
Providing either an explicit annotation or a subsequent constrained operation resolves the type:
fn main() {
let explicit: Option<i32> = None;
let mut inferred = None;
inferred = Some("ready");
println!("explicit = {explicit:?}, inferred = {inferred:?}");
}
explicit = None, inferred = Some("ready")
Conditional Extraction with if let, let ... else, and match
| Construct | Best Used When |
|---|---|
match opt { Some(v) => ..., None => ... } |
Both Some and None branches produce values or distinct actions |
if let Some(v) = opt { ... } |
You only need to act when a value is Some(v) |
let Some(v) = opt else { return ...; }; |
You want to bind v in the outer scope and diverge (return, break, continue, panic!) on None |
while let Some(v) = iter.next() { ... } |
Looping while an operation continues returning Some(v) |
fn format_port(raw: Option<u16>) -> String {
let Some(port) = raw else {
return String::from("using default port 80");
};
format!("listening on port {port}")
}
fn main() {
println!("{}", format_port(Some(8080)));
println!("{}", format_port(None));
}
listening on port 8080
using default port 80
In-Place Mutation with as_mut(), take(), and replace()
When working with a mutable Option<T>, standard methods let you modify or move out the inner Some(T) value without cloning:
let mut slot = Some(String::from("token"));
let extracted = slot.take();
Before .take() After .take()
slot: Some("token") slot: None
extracted: Some("token")
| Method | Signature | Effect on self |
Return Value |
|---|---|---|---|
opt.as_mut() |
&mut self -> Option<&mut T> |
Borrows inner T mutably in place |
Some(&mut v) or None |
opt.take() |
&mut self -> Option<T> |
Replaces *self with None |
Previous Some(v) or None |
opt.replace(v) |
&mut self, v: T -> Option<T> |
Replaces *self with Some(v) |
Previous Some(old) or None |
opt.get_or_insert(v) |
&mut self, v: T -> &mut T |
Inserts Some(v) if None |
&mut T to the stored value |
fn main() {
let mut cache: Option<String> = Some(String::from("v1"));
if let Some(val) = cache.as_mut() {
val.push_str("-patched");
}
println!("After as_mut: {cache:?}");
let old = cache.replace(String::from("v2"));
println!("Replaced old: {old:?}, current: {cache:?}");
let taken = cache.take();
println!("Taken value: {taken:?}, current: {cache:?}");
}
After as_mut: Some("v1-patched")
Replaced old: Some("v1-patched"), current: Some("v2")
Taken value: Some("v2"), current: None
Passing Some as a Constructor Function
Like Ok and Err, the tuple variant Some is also a function of type fn(T) -> Option<T>, so it can be passed directly to higher-order methods such as Iterator::map:
fn main() {
let ids = [101, 102, 103];
let optional_ids: [Option<i32>; 3] = ids.map(Some);
println!("{optional_ids:?}");
}
[Some(101), Some(102), Some(103)]