The ? operator returns early from a function when a Result is an error, and unwraps the success value otherwise.

Program

Play the program to double a selected input or report that parsing failed.

input
question_mark.rs
Replay: real traced execution (multi-file project)
fn double_input(text: &str) -> Result<i32, std::num::ParseIntError> {
    let value = text.parse::<i32>()?;
    Ok(value * 2)
}

fn main() {
    let input = "21";
    let result = double_input(input);
    let label = match result {
        Ok(value) => format!("double:{value}"),
        Err(_) => "parse error".to_string(),
    };
    println!("{label}");
}
fn double_input(text: &str) -> Result<i32, std::num::ParseIntError> {
    let value = text.parse::<i32>()?;
    Ok(value * 2)
}

fn main() {
    let input = "8";
    let result = double_input(input);
    let label = match result {
        Ok(value) => format!("double:{value}"),
        Err(_) => "parse error".to_string(),
    };
    println!("{label}");
}
fn double_input(text: &str) -> Result<i32, std::num::ParseIntError> {
    let value = text.parse::<i32>()?;
    Ok(value * 2)
}

fn main() {
    let input = "bad";
    let result = double_input(input);
    let label = match result {
        Ok(value) => format!("double:{value}"),
        Err(_) => "parse error".to_string(),
    };
    println!("{label}");
}
  1. input ← "21"

    6fn main() {7    let inpu→ "21"t = "21"; //@input="21", "8", "bad"8    let result = double_input(inpu"21"t);9    let label = match result {
  2. value ← 21

    1fn double_input(text: &str) -> Result<i32, std::num::ParseIntError> {2    let valu→ 21e = text.parse::<i32>()?;3    Ok(valu21e * 2)4}
  3. result ← Ok(42), label ← "double:42"

    7    let input = "21"; //@input="21", "8", "bad"8    let resul→ Ok(42)t = double_input(inpu"21"t);9    let labe→ "double:42"l = match resulOk(42)t {10        Ok(value) => format!("double:{value}"),11        Err(_) => "parse error".to_string(),12    };13    println!("{label}");14}
    outputdouble:42
  1. input ← "8"

    6fn main() {7    let inpu→ "8"t = "8";8    let result = double_input(inpu"8"t);9    let label = match result {
  2. value ← 8

    1fn double_input(text: &str) -> Result<i32, std::num::ParseIntError> {2    let valu→ 8e = text.parse::<i32>()?;3    Ok(valu8e * 2)4}
  3. result ← Ok(16), label ← "double:16"

    7    let input = "8";8    let resul→ Ok(16)t = double_input(inpu"8"t);9    let labe→ "double:16"l = match resulOk(16)t {10        Ok(value) => format!("double:{value}"),11        Err(_) => "parse error".to_string(),12    };13    println!("{label}");14}
    outputdouble:16
  1. input ← "bad"

    6fn main() {7    let inpu→ "bad"t = "bad";8    let result = double_input(inpu"bad"t);9    let label = match result {
  2. result ← Err(ParseIntError { kind: InvalidDigit }), label ← "parse error"

    7    let input = "bad";8    let resul→ Err(ParseIntError { kind: InvalidDigit })t = double_input(inpu"bad"t);9    let labe→ "parse error"l = match resulErr(ParseIntError { kind: InvalidDigit })t {10        Ok(value) => format!("double:{value}"),11        Err(_) => "parse error".to_string(),12    };13    println!("{label}");14}
    outputparse error

Follow the Shortcut

  1. The helper receives text from the caller.
  2. Parsing succeeds or fails.
  3. On success, ? gives the helper the number.
  4. On failure, ? returns the error to the caller immediately.
  5. The caller's match decides what to display.
caller -> helper -> parse ok  -> double value -> Ok(value)
caller <- helper <- parse err <- return early
? `?` returns the error immediately or gives back the success value.
propagation The parse error flows back to the caller as `Err`.
caller match The caller decides how to turn the result into user-facing text.

Exercise: question_mark.rs

Use ? in a helper that parses text, doubles it, and lets the caller format both outcomes