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 type T, allowing the compiler to infer Option<T> directly from value.
  • None carries no value, so the compiler must infer T from 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)]