What is this question mark operator about?

Rust

Rust Problem Overview


I'm reading the documentation for File:

//..
let mut file = File::create("foo.txt")?;
//..

What is the ? in this line? I do not recall seeing it in the Rust Book before.

Rust Solutions


Solution 1 - Rust

As you may have noticed, Rust does not have exceptions. It has panics, but their use for error-handling is discouraged (they are meant for unrecoverable errors).

In Rust, error handling uses Result. A typical example would be:

fn halves_if_even(i: i32) -> Result<i32, Error> {
    if i % 2 == 0 {
        Ok(i / 2)
    } else {
        Err(/* something */)
    }
}

fn do_the_thing(i: i32) -> Result<i32, Error> {
    let i = match halves_if_even(i) {
        Ok(i) => i,
        Err(e) => return Err(e),
    };

    // use `i`
}

This is great because:

  • when writing the code you cannot accidentally forget to deal with the error,
  • when reading the code you can immediately see that there is a potential for error right here.

It's less than ideal, however, in that it is very verbose. This is where the question mark operator ? comes in.

The above can be rewritten as:

fn do_the_thing(i: i32) -> Result<i32, Error> {
    let i = halves_if_even(i)?;

    // use `i`
}

which is much more concise.

What ? does here is equivalent to the match statement above with an addition. In short:

  1. It unpacks the Result if OK
  2. It returns the error if not, calling Into::into on the error value to potentially convert it to another type.

It's a bit magic, but error handling needs some magic to cut down the boilerplate, and unlike exceptions it is immediately visible which function calls may or may not error out: those that are adorned with ?.

One example of the magic is that this also works for Option:

// Assume
// fn halves_if_even(i: i32) -> Option<i32>

fn do_the_thing(i: i32) -> Option<i32> {
    let i = halves_if_even(i)?;

    // use `i`
}

The ? operator, stabilized in Rust version 1.13.0 is powered by the (unstable) Try trait.

See also:

Solution 2 - Rust

It is a postfix operator that unwraps Result<T, E> and Option<T> values.

If applied to Result<T, E>, it unwraps the result and gives you the inner value, propagating the error to the calling function.

let number = "42".parse::<i32>()?;
println!("{:?}", number); // 42

When applied to an Option<T>, it propagates None to the caller, leaving you the content of the Some branch to deal with.

let val = Some(42)?;
println!("{:?}", val); // 42

The ? operator can only be used in a function that returns Result or Option like so:

use std::num::ParseIntError;

fn main() -> Result<(), ParseIntError> {
    let number = "42".parse::<i32>()?;
    println!("{:?}", number);
    Ok(())
}

It is a convenience offered by Rust, that eliminates boilerplate code and makes function's implementation simpler.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAngel AngelView Question on Stackoverflow
Solution 1 - RustMatthieu M.View Answer on Stackoverflow
Solution 2 - RustsnnsnnView Answer on Stackoverflow