TL;DR:
The ? operator returns early from a function if the operation it was called on was not successful.
It returns the error.
Ever seen a line of Rust code with a rising inflection, with an inquisitive tone?
That line is questioning its existence
let f = File::open("username.txt")?;
The ? in Rust is the questionmark operator.
It's a piece of syntax that replaces a very common case of error handling.
The following piece of code deals with 2 operations that can fail.
It tries to open a file and read the username inside.
If any of the operations fail, it will stop execution of the function and return information about that failure.
This usecase is called error propagation.
fn read_username_from_file() -> Result<String, io::Error> {
    let f = File::open("username.txt");
    let mut f = match f {
        Ok(file) => file,
        Err(e) => return Err(e),
    };
    let mut s = String::new();
    match f.read_to_string(&mut s) {
        Ok(_) => Ok(s),
        Err(e) => Err(e),
    }
}
The
Result<T, E>type, is anenumwith 2 variants.
BothTandEare placeholders for types.enum Result<T, E> { Ok(T), Err(E), }
The read_username_from_file function returns a Result<String, io::Error>.
That means the returned value from that function will either be an Ok that holds a String,
or an Err that holds an instance of io::Error.
Inside read_username_from_file there is a call to File::open, which returns a Result type.
- It can return an Okwith a file handle inside.
- It can return an Errwith an error inside (anio::Error).
To deal with that possibility, the code calls a match on the result of the File::open function.
- On Ok, it returns what was inside theOkfrom thematch
- On Err, it early returns from the entire function with thatErras returned value.
To put it differently: it either unwraps the Ok, or it quits the function and returns the Err.
Later in the read_username_from_file function another operation that returns a Result is called.
Very similar logic is applied there.
- If the returned value is an Ok, this signals a successful completion of the function.sis wrapped in anOkand returned from thematch.
- If the returned value is an Err, it is returned from thematch. (theErrcontains anio::Error)
Since this is the last expression of the function,
the return keyword is no longer needed as the returned value from the match is also the returned value from the function.
Using ?, the function now looks like this:
fn read_username_from_file() -> Result<String, io::Error> {
    let mut f = File::open("username.txt")?;
    let mut s = String::new();
    f.read_to_string(&mut s)?;
    Ok(s)
}
The ? operator can be used on a Result type if the function it's in returns a Result as well.
Every time you see a ?, that's a possible early return from the function the ? is used in.
The ? is a shorthand for the behaviour in that first match statement.
- on Ok, it unwraps theOkand evaluates to the value inside
- on Err, it returns from the function with thatErr(that holds anio::Errorin this case)
The possible Err from File::open is returned from the function.
If there is no error, f will hold the file handle the Ok contained and execution of the function continues.
The possible Err from read_to_string is returned from the function.
If there is no error, the method finished successfully and execution of the function continues.
The value the Ok held is ignored by turning the f.read_to_string(&mut s)? expression into a statement.
If the code reaches a point after that line, it means the result was Ok.
The last expression in this rewritten function is an Ok with a String inside: Ok(s).
When using the ? on a Result, I call it the Annie operator
There is a slight difference between the 2 pieces of code.
It will send the error contained in Err(error) through the From::from method.
So if the Result variant the ? is called on is Err(error), the function will return Err(From::from(error)).
This example with the ? operator:
let mut f = File::open("username.txt")?;
is equivalent to:
let mut f = match File::open("username.txt") {
    Ok(result) => result,
    Err(err) => return Err(From::from(err)),
}
? can also be used on the Option type. It will then unwrap Some, or propagate None.
fn get_number(name: String) -> Option<String> {
 if name == "Carly" {
     Some(String::from("1-800-JEPSEN"))
 } else {
     None
 }
}
fn call_me_maybe(name: String) -> Option<u8> {
    let number = get_number(name)?;
    // calling the number
    Some(0)
}
The returned type of a function must be compatible with the returned type from the ? operator.
In practice, that often means the function ? is called in returns a Result<T, E>.
Because ? possibly exits the function with an Err(error), that error must be compatible with the E in the Result the function returns.
 

 
    
Top comments (0)