What is the question mark (?) operator?
In Rust, the question mark (?
) operator is used as an alternate error propagation method for functions that yield Result
or Option
types. The ?
operator is a shortcut that minimizes the amount of code required in a function to quickly return Err
or None
from the types Result<T, Err>
, or Option
.
Error propagation is the process of “propagating,” spreading up, or returning error information identified in code that is often triggered by a caller function in order for the caller function to resolve the problem correctly.
Let’s have a look at how error propagation works in code using the example below:
fn main() -> Result<()> {
let i = match halves_if_even(i) {
Ok(i) => i,
Err(e) => return Err(e),
};
println!("halves of i = {}", i);
match submit_number(i) {
Ok(_) => {
println!("Successfully");
Ok(())
},
Err(e) => return Err(e),
}
}
fn halves_if_even(i: i32) -> Result<i32, Error> {
if i % 2 == 0 {
Ok(i / 2)
} else {
Err(/* something */)
}
}
fn submit_number(i: i32) -> Result<(), Error> {
// ...
}
However, in that it is very verbose. This is where the question mark operator ?
comes in.
fn main() -> Result<()> {
let i = 35;
let i = halves_if_even(i)?;
println!("halves of i = {}", i);
submit_number(i)?;
Ok(())
}
fn halves_if_even(i: i32) -> Result<i32, Error> {
// ...
}
fn submit_number(i: i32) -> Result<(), Error> {
// ...
}
What ?
does here is equivalent to the match
statement above with an addition. This operator desugars into a pattern match.
The nice thing about this code is that one can easily see and audit potential errors: for example, I can see that halves_if_even
may result in an error, and a keen-eyed reviewer may see the potential data loss.
Even better, in the event of an error, I can do some type of recovery by opting to match rather than forward the error.
fn main() -> Result<()> {
let i = 35;
let i = halves_if_even(i)?;
match submit_number(i) {
Ok(_) => Ok(()),
Err(err) => recover_from_error(err),
}
}