Is Option<i32> unwind safe? - rust

I am implementing a wrapper of a C library which takes callbacks and the callbacks will be implemented in Rust. Given that panicking in Rust when calling from C is undefined behavior, I want to catch any potential Rust panics before they get into C.
I have been reading about std::panic::catch_unwind. The wrapper is performance sensitive and I would prefer to avoid using types like Mutex. I would like to hold my result in an Option<i32> and set this to Some(value) in the case where there is no panic. None will indicate that the function did not execute successfully and thus, a panic must have occurred.
Is Option<i32> unwind safe? If not, under what conditions would there be a problem? Can I wrap it in std::panic::AssertUnwindSafe?
Here is an example where I used AssertUnwindSafe to wrap the entire closure.
use std::panic::{self, AssertUnwindSafe};
fn random_function_that_might_panic(a: i32) -> i32 {
if a == 42 {
panic!("did you forget a towel?");
}
a * 2
}
fn do_not_panic(a: i32) {
let mut result = None;
let unwind_state = panic::catch_unwind(AssertUnwindSafe(|| {
result = Some(random_function_that_might_panic(a)); // get result, but this could panic
}));
match unwind_state {
Ok(()) => {
match result {
Some(value) => {
println!("Result: {:?}", value);
}
None => {
// this should never happen...
println!("No result but no panic?");
}
}
}
Err(e) => {
println!("caught panic: {:?}", e);
}
}
}
fn main() {
do_not_panic(1);
do_not_panic(2);
do_not_panic(3);
do_not_panic(42);
}
(See the above on the playground.)
I could not figure out how to wrap just Option<i32> in AssertUnwindSafe, so here I wrapped the whole closure. How would I wrap just the Option<i32>?

Is Option<i32> unwind safe?
Yes. There's no reason to ask a human this question when you can ask the compiler:
fn implements<T: std::panic::UnwindSafe>() {}
fn main() {
implements::<Option<i32>>();
}
Your real question should be:
Is &mut Option<i32> unwind safe?
This is not, but you probably already knew that. I'm guessing that you got this compiler error before you added AssertUnwindSafe which tells you it isn't safe:
error[E0277]: the trait bound `&mut std::option::Option<i32>: std::panic::UnwindSafe` is not satisfied in `[closure#src/main.rs:12:44: 14:6 result:&mut std::option::Option<i32>, a:&i32]`
--> src/main.rs:12:24
|
12 | let unwind_state = panic::catch_unwind(|| {
| ^^^^^^^^^^^^^^^^^^^ the type &mut std::option::Option<i32> may not be safely transferred across an unwind boundary
|
= help: within `[closure#src/main.rs:12:44: 14:6 result:&mut std::option::Option<i32>, a:&i32]`, the trait `std::panic::UnwindSafe` is not implemented for `&mut std::option::Option<i32>`
= note: required because it appears within the type `[closure#src/main.rs:12:44: 14:6 result:&mut std::option::Option<i32>, a:&i32]`
= note: required by `std::panic::catch_unwind`
I'd write your code as this, for what it's worth:
fn do_not_panic(a: i32) {
let result = panic::catch_unwind(|| random_function_that_might_panic(a)).ok();
match result {
Some(value) => {
println!("Result: {:?}", value);
}
None => {
println!("caught panic");
}
}
}
No mutable variables, no extra nesting, no "this should never happen" comments.
See also:
The documentation for UnwindSafe
Is there a way to tell the Rust compiler to call drop on partially-initialized array elements when handling a panic?

Related

What is the difference between the ? operator and returning Err(e)? [duplicate]

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:
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:
It unpacks the Result if OK
It returns the error if not, calling From::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:
Is the question mark operator ? equivalent to the try! macro?
Why do try!() and ? not compile when used in a function that doesn't return Option or Result?
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:
If it is a success, it unwraps the Result to get the success value inside. Value is assigned to the variable file
If the result is an error, the error is NOT assigned to the variable file. Error is returned by the function to the caller
Using ? 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)),
}
}

Is there a safe way to map an enum variant to another with just a mutable reference to it in Rust?

Consider the following code:
enum Deferred<F, T> {
Fn(F),
Ready(T),
}
impl<F, T> Deferred<F, T>
where
F: FnOnce() -> T,
T: Clone,
{
fn get_clone(&mut self) -> T {
let clone;
*self = match *self {
Self::Fn(f) => { // `f` moved here
let data = f(); // this needs to consume `f`
clone = data.clone();
Self::Ready(data)
},
Self::Ready(data) => { // `data` moved here
clone = data.clone();
Self::Ready(data)
},
};
clone
}
}
The compiler complains about the *self = match *self {...}; statement because its right hand side takes ownership of contents of self. Is there a way to accomplish this behaviour with just a mutable reference to self?
I found a workaround using F: FnMut() -> T so that f doesn't have to be moved but this approach clearly has its limitations.
I also tried what the answer to Is there a safe way to temporarily retrieve an owned value from a mutable reference in Rust? suggested but it led to an issue with initialization of clone (the compiler could no longer reason that the match statement would initialize clone because the code was moved into a closure) so I had to use MaybeUninit with unsafe.
At that point it was better to read/write self through a raw pointer:
unsafe {
std::ptr::write(
self as *mut Self,
match std::ptr::read(self as *const Self) {
Self::Fn(f) => {
let data = f();
clone = data.clone();
Self::Ready(data)
},
Self::Ready(data) {
clone = data.clone();
Self::Ready(data)
},
}
);
}
It is not possible to do this in a straightforward safe fashion, because while f is being executed there is no possible valid value of Deferred as defined: you don't yet have a T to go in Deferred::Ready, and you're consuming the F so you can't have Deferred::Fn.
If you use the take_mut crate or similar, that accomplishes this by replacing panicking with aborting, so the invalid state can never be observed. But, in your case, I would recommend introducing a third state to the enum instead — this changes the semantics but in a way that, shall we say, respects the fact that f can fail.
enum Deferred<F, T> {
Fn(F),
Ready(T),
Failed,
}
impl<F, T> Deferred<F, T>
where
F: FnOnce() -> T,
T: Clone,
{
fn get_clone(&mut self) -> T {
match std::mem::replace(self, Self::Failed) {
Self::Ready(data) => {
let clone = data.clone();
*self = Self::Ready(data);
clone
},
Self::Fn(f) => {
let data = f();
*self = Self::Ready(data.clone());
data
}
Self::Failed => {
panic!("A previous call failed");
}
}
}
}
With this solution, the very first thing we do is swap out *self for Self::Failed, so we own the Deferred value and can freely move out the non-clonable F value. Notice that the expression being matched is not a borrow of self, so we aren't blocked from further modifying *self.
This particular solution does have a disadvantage: it's unnecessarily writing to *self on every call (which is unobservable, but could reduce performance). We can fix that, by separating the decision of what to do from doing it, but this requires writing a second pattern match to extract the value:
impl<F, T> Deferred<F, T>
where
F: FnOnce() -> T,
T: Clone,
{
fn get_clone(&mut self) -> T {
match self {
Self::Ready(data) => {
return data.clone();
},
Self::Failed => {
panic!("A previous call failed");
}
Self::Fn(_) => {
// Fall through below, relinquishing the borrow of self.
}
}
match std::mem::replace(self, Self::Failed) {
Self::Fn(f) => {
let data = f();
*self = Self::Ready(data.clone());
data
}
_ => unreachable!()
}
}
}
The fundamental problem here is trying to take ownership of the value in the enum while simultaneously having a live reference (via &mut self) to it. Safe Rust code will not let you do that, which is the reason for the compiler error. This is also a problem with your unsafe-solution: What if any code inside one of the match-arms panics? Then the owned value created via ptr::read gets dropped, the stack unwinds, and the caller might catch that panic and is then perfectly capable of observing the Deferred it owns (of which it gave a &mut to get_clone()) after it has been dropped, that is, in an invalid state and therefor causing Undefined Behaviour (see the docs for ptr::read for an example).
What must be achieved therefore is to hold &mut self in a valid/defined state while taking ownership. You can do that by having a cheap or possibly even free variant on Deferred, which is put into &mut self while the new value is being constructed.
For instance:
#[derive(Debug)]
enum FooBar {
Foo(String),
Bar(&'static str),
}
impl FooBar {
fn switch(&mut self) {
// notice here
*self = match std::mem::replace(self, FooBar::Bar("temporary")) {
FooBar::Foo(_) => FooBar::Bar("default"),
FooBar::Bar(s) => FooBar::Foo(s.to_owned()),
}
}
}
fn main() {
let mut s = FooBar::Bar("hello");
s.switch();
println!("{:?}", s);
}
Here, we use std::mem::replace to switch the value behind &mut self with a cheaply constructed temporary value; replace() returns ownership of the original value, which we match on to construct a new value; the value returned by the match-expression is then put into place via the *self-assignment. If everything goes well, the FooBar::Bar("temporary") is not ever observed; but it could be observed if the match panicked; but even then, the code is at least safe. In case your match can't unwind at all, the compiler might even be able to eliminate that useless store entirely.
Coming back to your original code, I don't see how to construct a Deferred without T being Default, as you can neither construct the Fn nor the Ready case in a safe way. Either that can be added via an additional bound, or you can add a third variant Empty to your enum.
As I see it, the difference between your problem and the one that you referenced is that you also want to return the inner T while replacing self. Which is something that the there proposed crate take_mut does not provide. However, I noticed that it seems a bit unmaintained. So, I had a short look around, and found the replace_with crate instead. It basically does the same as take_mut, but it also has a replace_with_or_abort_and_return function -- exactly what you need:
use replace_with::replace_with_or_abort_and_return;
impl<F, T> Deferred<F, T>
where
F: FnOnce() -> T,
T: Clone,
{
fn get_clone(&mut self) -> T {
replace_with_or_abort_and_return(self, |s| match s {
Self::Fn(f) => {
// `f` moved here
let data = f(); // this needs to consume `f`
let clone = data.clone();
(clone, Self::Ready(data))
}
Self::Ready(data) => {
// `data` moved here
let clone = data.clone();
(clone, Self::Ready(data))
}
})
}
}
Tho, notice, that taking a value out of a mutable borrow, has the potential of UB that is specifically if your f() panics, because self would be left in an uninitialized state. Therefore, we get this slightly cumbersome function name, which indicates that if f() panic, the program will be aborted (which is the safe thing to do). However, if you expect f() to panic and don't want to abort the program, you can instead provide a default for self via the replace_with_and_return or replace_with_or_default_and_return function.

How can you use an immutable Option by reference that contains a mutable reference?

Here's a Thing:
struct Thing(i32);
impl Thing {
pub fn increment_self(&mut self) {
self.0 += 1;
println!("incremented: {}", self.0);
}
}
And here's a function that tries to mutate a Thing and returns either true or false, depending on if a Thing is available:
fn try_increment(handle: Option<&mut Thing>) -> bool {
if let Some(t) = handle {
t.increment_self();
true
} else {
println!("warning: increment failed");
false
}
}
Here's a sample of usage:
fn main() {
try_increment(None);
let mut thing = Thing(0);
try_increment(Some(&mut thing));
try_increment(Some(&mut thing));
try_increment(None);
}
As written, above, it works just fine (link to Rust playground). Output below:
warning: increment failed
incremented: 1
incremented: 2
warning: increment failed
The problem arises when I want to write a function that mutates the Thing twice. For example, the following does not work:
fn try_increment_twice(handle: Option<&mut Thing>) {
try_increment(handle);
try_increment(handle);
}
fn main() {
try_increment_twice(None);
let mut thing = Thing(0);
try_increment_twice(Some(&mut thing));
try_increment_twice(None);
}
The error makes perfect sense. The first call to try_increment(handle) gives ownership of handle away and so the second call is illegal. As is often the case, the Rust compiler yields a sensible error message:
|
24 | try_increment(handle);
| ------ value moved here
25 | try_increment(handle);
| ^^^^^^ value used here after move
|
In an attempt to solve this, I thought it would make sense to pass handle by reference. It should be an immutable reference, mind, because I don't want try_increment to be able to change handle itself (assigning None to it, for example) only to be able to call mutations on its value.
My problem is that I couldn't figure out how to do this.
Here is the closest working version that I could get:
struct Thing(i32);
impl Thing {
pub fn increment_self(&mut self) {
self.0 += 1;
println!("incremented: {}", self.0);
}
}
fn try_increment(handle: &mut Option<&mut Thing>) -> bool {
// PROBLEM: this line is allowed!
// (*handle) = None;
if let Some(ref mut t) = handle {
t.increment_self();
true
} else {
println!("warning: increment failed");
false
}
}
fn try_increment_twice(mut handle: Option<&mut Thing>) {
try_increment(&mut handle);
try_increment(&mut handle);
}
fn main() {
try_increment_twice(None);
let mut thing = Thing(0);
try_increment_twice(Some(&mut thing));
try_increment_twice(None);
}
This code runs, as expected, but the Option is now passed about by mutable reference and that is not what I want:
I'm allowed to mutate the Option by reassigning None to it, breaking all following mutations. (Uncomment line 12 ((*handle) = None;) for example.)
It's messy: There are a whole lot of extraneous &mut's lying about.
It's doubly messy: Heaven only knows why I must use ref mut in the if let statement while the convention is to use &mut everywhere else.
It defeats the purpose of having the complicated borrow-checking and mutability checking rules in the compiler.
Is there any way to actually achieve what I want: passing an immutable Option around, by reference, and actually being able to use its contents?
You can't extract a mutable reference from an immutable one, even a reference to its internals. That's kind of the point! Multiple aliases of immutable references are allowed so, if Rust allowed you to do that, you could have a situation where two pieces of code are able to mutate the same data at the same time.
Rust provides several escape hatches for interior mutability, for example the RefCell:
use std::cell::RefCell;
fn try_increment(handle: &Option<RefCell<Thing>>) -> bool {
if let Some(t) = handle {
t.borrow_mut().increment_self();
true
} else {
println!("warning: increment failed");
false
}
}
fn try_increment_twice(handle: Option<RefCell<Thing>>) {
try_increment(&handle);
try_increment(&handle);
}
fn main() {
let mut thing = RefCell::new(Thing(0));
try_increment_twice(Some(thing));
try_increment_twice(None);
}
TL;DR: The answer is No, I can't.
After the discussions with #Peter Hall and #Stargateur, I have come to understand why I need to use &mut Option<&mut Thing> everywhere. RefCell<> would also be a feasible work-around but it is no neater and does not really achieve the pattern I was originally seeking to implement.
The problem is this: if one were allowed to mutate the object for which one has only an immutable reference to an Option<&mut T> one could use this power to break the borrowing rules entirely. Concretely, you could, essentially, have many mutable references to the same object because you could have many such immutable references.
I knew there was only one mutable reference to the Thing (owned by the Option<>) but, as soon as I started taking references to the Option<>, the compiler no longer knew that there weren't many of those.
The best version of the pattern is as follows:
fn try_increment(handle: &mut Option<&mut Thing>) -> bool {
if let Some(ref mut t) = handle {
t.increment_self();
true
}
else {
println!("warning: increment failed");
false
}
}
fn try_increment_twice(mut handle: Option<&mut Thing>) {
try_increment(&mut handle);
try_increment(&mut handle);
}
fn main() {
try_increment_twice(None);
let mut thing = Thing(0);
try_increment_twice(Some(&mut thing));
try_increment_twice(None);
}
Notes:
The Option<> holds the only extant mutable reference to the Thing
try_increment_twice() takes ownership of the Option<>
try_increment() must take the Option<> as &mut so that the compiler knows that it has the only mutable reference to the Option<>, during the call
If the compiler knows that try_increment() has the only mutable reference to the Option<> which holds the unique mutable reference to the Thing, the compiler knows that the borrow rules have not been violated.
Another Experiment
The problem of the mutability of Option<> remains because one can call take() et al. on a mutable Option<>, breaking everything following.
To implement the pattern that I wanted, I need something that is like an Option<> but, even if it is mutable, it cannot be mutated. Something like this:
struct Handle<'a> {
value: Option<&'a mut Thing>,
}
impl<'a> Handle<'a> {
fn new(value: &'a mut Thing) -> Self {
Self {
value: Some(value),
}
}
fn empty() -> Self {
Self {
value: None,
}
}
fn try_mutate<T, F: Fn(&mut Thing) -> T>(&mut self, mutation: F) -> Option<T> {
if let Some(ref mut v) = self.value {
Some(mutation(v))
}
else {
None
}
}
}
Now, I thought, I can pass around &mut Handle's all day long and know that someone who has a Handle can only mutate its contents, not the handle itself. (See Playground)
Unfortunately, even this gains nothing because, if you have a mutable reference, you can always reassign it with the dereferencing operator:
fn try_increment(handle: &mut Handle) -> bool {
if let Some(_) = handle.try_mutate(|t| { t.increment_self() }) {
// This breaks future calls:
(*handle) = Handle::empty();
true
}
else {
println!("warning: increment failed");
false
}
}
Which is all fine and well.
Bottom line conclusion: just use &mut Option<&mut T>

Why can't I use the ? operator in my main function on a function that returns an Option?

Using this file:
use std::env;
fn main() {
println!("{}", env::args().nth(3)?);
}
I get this error:
error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
--> src/main.rs:4:20
|
4 | println!("{}", env::args().nth(3)?);
| ^^^^^^^^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()`
|
= help: the trait `std::ops::Try` is not implemented for `()`
= note: required by `std::ops::Try::from_error`
However this is confusing because nth does return Option:
fn nth(&mut self, n: usize) -> Option<Self::Item>
Am I misunderstanding the documentation or is this a bug?
The return type of main must implement std::process::Termination(currently it's an unstable trait). If you look at the end of the documentation, you will see:
impl Termination for !
impl Termination for ()
impl Termination for ExitCode
impl<E: Debug> Termination for Result<!, E>
impl<E: Debug> Termination for Result<(), E>
If you want to return an Option you must implement the trait on it. This is not practical because you can't implement a trait on foreign type, so the best solution is to convert Option<T> to Result<T, E>:
use std::env;
fn main() -> Result<(), Box<std::error::Error>> {
println!("{}", env::args().nth(3).ok_or("Missing argument")?);
Ok(())
}
See also:
Why do try!() and ? not compile when used in a function that doesn't return Option or Result?
The ? operator will cause the function containing it to return None if the value the ? is applied to is None.
This means you can write
fn not_main() -> Option<()> {
println!("{}", std::env::args().nth(3)?);
Ok(())
}
since nth returns an Option<Item> and not_main returns an Option<()>.
However, your main does not return an Option, hence ? can't work inside it.
How you work around this will depend on what you want to do in the case of a missing argument. The most brutal solution is to unwrap instead - which will cause your code to panic.
fn main() {
println!("{}", env::args().nth(3).unwrap())
}
An alternative is to match and handle the missing case
fn main() {
match std::env::args().nth(3) {
Some(ref v) => println!("{}", v),
None => println!("Missing argument"),
}
}
Since Option supports Debug you could print the debug version - which will output None, or Some("arg3").
fn main() {
println!("{:?}", std::env::args().nth(3));
}
If you really want to use ? on a Option value in main, you probably need to implement you own Option.
In your case, nothing::Probably is a better Option.
Example (you need nightly toolchain to run it):
use nothing::{Nothing, Probably, Something};
fn get_args() -> Probably<Vec<String>> {
match std::env::args().skip(1).collect::<Vec<String>>() {
args # _ if args.len() > 0 => Something(args),
_ => Nothing,
}
}
fn main() -> Probably<Vec<String>> {
let some_args = get_args();
println!("some_args = {some_args:?}");
let args = some_args?; // <- it returns here if some_args is Nothing
println!("args = {args:?}");
Something(args)
}
It works because Probably implements std::process::Termination so you can return it from you main function. Additionally it implements std::ops::Try so you can use ? on it.

What is this question mark operator about?

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:
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:
It unpacks the Result if OK
It returns the error if not, calling From::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:
Is the question mark operator ? equivalent to the try! macro?
Why do try!() and ? not compile when used in a function that doesn't return Option or Result?
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:
If it is a success, it unwraps the Result to get the success value inside. Value is assigned to the variable file
If the result is an error, the error is NOT assigned to the variable file. Error is returned by the function to the caller
Using ? 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)),
}
}

Resources