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.
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.
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:
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:
Result
if OKFrom::from
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:
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.
It is used for propagating errors
. Sometimes we write code that might fail but we do not want to catch and handle error immediately. Your code will not be readable if you have too much code to handle the error in every place. Instead, if an error occurs, we might want to let our caller deal with it. We want errors to propagate up the call stack.
// file type is Result if "?" is not used
// file:Result<File,Error>
let mut file = File::create("foo.txt");
// file type is File if "?" is used
// file:File
let mut file = File::create("foo.txt")?;
// if an error occurs, code after this line will not be executed
// function will return the error
The behavior of ?
depends on whether this function returns a successful result or an error result:
file
file
. Error is returned by the function to the callerUsing ?
same as this code
let mut file = match File::create("foo.txt") {
Err(why) => panic!("couldn't create {}: {}", display, why),
Ok(file) => file,
};
?
also works similarly with the Option type. In a function that returns Option, you
can use ? to unwrap a value and return early in the case of None :
The existing answers are all great! I would like to give a small code snippet to demo the use of From::from()
behand this question mark:
fn _parse(str: &str) -> Result<i32, &str> {
if let Ok(num) = str.parse::<i32>() {
Ok(num)
} else {
Err(str)
}
}
fn parse(str: &str) -> Result<(), String> {
let num = _parse(str)?;
println!("{}", num);
Ok(())
}
The use of ?
in function parse()
can be manually rewritten as:
fn parse(str: &str) -> Result<(), String> {
match _parse(str) {
Ok(n) => {
println!("{}", n);
Ok(())
}
Err(str) => Err(<String as From<&str>>::from(str)),
}
}