← Back to Rust Course | Chapter 10: Error Handling | Lesson 2 of 6

The ? Operator

The ? mark is a shortcut that says "if this failed, stop here and hand the failure up to whoever called me."
Syntax
rust
fn name() -> Result<T, E> {
    let value = fallible()?;
    Ok(value)
}

Basic Use of the ? Operator

Placing ? after an expression that returns Result unwraps the Ok value automatically, or immediately returns the Err from the enclosing function if it failed.

Example: Basic Use of the ? Operator

markup
// Define the function `parse_number` taking `s` and returning a `Result<i32, std::num::ParseIntError>`
fn parse_number(s: &str) -> Result<i32, std::num::ParseIntError> {
    // Declare an immutable variable `n`, set to `s.parse::<i32>()?`
    let n = s.parse::<i32>()?;
    Ok(n * 2)
}

// Entry point: execution of the program starts here
fn main() {
    // Match on `parse_number("21")` and branch by pattern
    match parse_number("21") {
        Ok(v) => println!("Doubled: {}", v),
        Err(e) => println!("Error: {:?}", e),
    }
}

Chaining Multiple Fallible Calls

The ? operator shines when chaining several fallible operations, letting each step propagate its error immediately instead of requiring a nested match for every call.

Example: Chaining Multiple Fallible Calls

markup
// Define the function `parse_and_add` taking `a`, `b` and returning a `Result<i32, std::num::ParseIntError>`
fn parse_and_add(a: &str, b: &str) -> Result<i32, std::num::ParseIntError> {
    // Declare an immutable variable `x`, set to `a.parse::<i32>()?`
    let x = a.parse::<i32>()?;
    // Declare an immutable variable `y`, set to `b.parse::<i32>()?`
    let y = b.parse::<i32>()?;
    Ok(x + y)
}

// Entry point: execution of the program starts here
fn main() {
    // Print "{:?}" to the console, substituting in `parse_and_add("3", "4")` (with a trailing newline)
    println!("{:?}", parse_and_add("3", "4"));
}

The Enclosing Function Must Return a Compatible Type

? can only be used inside a function whose return type is itself a Result (or Option) with a compatible error type, since it needs somewhere to return the propagated error to.

Example: The Enclosing Function Must Return a Compatible Type

markup
// Define the function `compute` taking `input` and returning a `Result<i32, std::num::ParseIntError>`
fn compute(input: &str) -> Result<i32, std::num::ParseIntError> {
    // Declare an immutable variable `value` (an integer), set to `input.parse()?`
    let value: i32 = input.parse()?;
    Ok(value + 100)
}

// Entry point: execution of the program starts here
fn main() {
    // Print "{:?}" to the console, substituting in `compute("5")` (with a trailing newline)
    println!("{:?}", compute("5"));
    // Print "{:?}" to the console, substituting in `compute("oops")` (with a trailing newline)
    println!("{:?}", compute("oops"));
}

Using ? with Option

The ? operator also works on Option, returning None early from the function if the value is None, mirroring how it works with Result.

Example: Using ? with Option

markup
// Define the function `first_char_uppercase` taking `s` and returning a `Option<char>`
fn first_char_uppercase(s: &str) -> Option<char> {
    // Declare an immutable variable `c`, set to `s.chars().next()?`
    let c = s.chars().next()?;
    Some(c.to_ascii_uppercase())
}

// Entry point: execution of the program starts here
fn main() {
    // Print "{:?}" to the console, substituting in `first_char_uppercase("rust")` (with a trailing newline)
    println!("{:?}", first_char_uppercase("rust"));
    // Print "{:?}" to the console, substituting in `first_char_uppercase("")` (with a trailing newline)
    println!("{:?}", first_char_uppercase(""));
}
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
Common Mistakes
  1. Using ? inside a function whose return type is not Result (or Option), which fails to compile.
  2. Forgetting ? only propagates the error -- it does not itself print anything or crash the program.
  3. Mixing incompatible error types across multiple ? calls without a conversion, which the compiler will reject unless the types unify.
Chapter Summary
  • ? after a Result-returning expression unwraps Ok values or returns early with the Err.
  • The enclosing function must itself return a compatible Result (or Option) type for ? to be used.
  • ? dramatically reduces boilerplate compared to manually matching every fallible call.
  • ? can also be used with Option, returning None early if the value is None.
🔒

Chapter Quiz — Complete all 6 topics to unlock

0/6 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.