It's easy to crash at runtime with unwrap
:
fn main() {
c().unwrap();
}
fn c() -> Option<i64> {
None
}
Result:
Compiling playground v0.0.1 (file:///playground)
Running `target/debug/playground`
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', ../src/libcore/option.rs:325
note: Run with `RUST_BACKTRACE=1` for a backtrace.
error: Process didn't exit successfully: `target/debug/playground` (exit code: 101)
Is unwrap
only designed for quick tests and proofs-of-concept?
I can not affirm "My program will not crash here, so I can use unwrap
" if I really want to avoid panic!
at runtime, and I think avoiding panic!
is what we want in a production application.
In other words, can I say my program is reliable if I use unwrap
? Or must I avoid unwrap
even if the case seems simple?
I read this answer:
It is best used when you are positively sure that you don't have an error.
But I don't think I can be "positively sure".
I don't think this is an opinion question, but a question about Rust core and programming.
unwrap can give good error messages, printing the error value and providing a backtrace, but in other configurations and deployments you might not see a backtrace and the error value might not be useful.
To “unwrap” something in Rust is to say, “Give me the result of the computation, and if there was an error, panic and stop the program.” It would be better if we showed the code for unwrapping because it is so simple, but to do that, we will first need to explore the Option and Result types.
While the whole “error handling”-topic is very complicated and often opinion based, this question can actually be answered here, because Rust has rather narrow philosophy. That is:
panic!
for programming errors (“bugs”)Result<T, E>
and Option<T>
for expected and recoverable errors
One can think of unwrap()
as converting between those two kinds of errors (it is converting a recoverable error into a panic!()
). When you write unwrap()
in your program, you are saying:
At this point, a
None
/Err(_)
value is a programming error and the program is unable to recover from it.
For example, say you are working with a HashMap
and want to insert a value which you may want to mutate later:
age_map.insert("peter", 21);
// ...
if /* some condition */ {
*age_map.get_mut("peter").unwrap() += 1;
}
Here we use the unwrap()
, because we can be sure that the key holds a value. It would be a programming error if it didn't and even more important: it's not really recoverable. What would you do when at that point there is no value with the key "peter"
? Try inserting it again ... ?
But as you may know, there is a beautiful entry
API for the maps in Rust's standard library. With that API you can avoid all those unwrap()
s. And this applies to pretty much all situations: you can very often restructure your code to avoid the unwrap()
! Only in a very few situation there is no way around it. But then it's OK to use it, if you want to signal: at this point, it would be a programming bug.
There has been a recent, fairly popular blog post on the topic of “error handling” whose conclusion is similar to Rust's philosophy. It's rather long but worth reading: “The Error Model”. Here is my try on summarizing the article in relation to this question:
In summary: use unwrap()
when you are sure that the recoverable error that you get is in fact unrecoverable at that point. Bonus points for explaining “why?” in a comment above the affected line ;-)
In other words, can I say my program is reliable if I use unwrap? Or must I avoid unwrap even if the case seems simple?
I think using unwrap
judiciously is something you have to learn to handle, it can't just be avoided.
My rhetorical question barrage would be:
(1) is like unwrap, indexing panics if you make a contract violation and try to index out of bounds. This would be a bug in the program, but it doesn't catch as much attention as a call to unwrap
.
(2) is like unwrap, integer division panics if the divisor is zero.
(3) is unlike unwrap, addition does not check for overflow in release builds, so it may silently result in wraparound and logical errors.
Of course, there are strategies for handling all of these without leaving panicky cases in the code, but many programs simply use for example bounds checking as it is.
There are two questions folded into one here:
panic!
acceptable in productionunwrap
acceptable in productionpanic!
is a tool that is used, in Rust, to signal irrecoverable situations/violated assumptions. It can be used to either crash a program that cannot possibly continue in the face of this failure (for example, OOM situation) or to work around the compiler knowing it cannot be executed (at the moment).
unwrap
is a convenience, that is best avoided in production. The problem about unwrap
is that it does not state which assumption was violated, it is better instead to use expect("")
which is functionally equivalent but will also give a clue as to what went wrong (without opening the source code).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With