Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should I avoid unwrap in production application?

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.

like image 312
rap-2-h Avatar asked Sep 13 '16 19:09

rap-2-h


People also ask

Why not to use unwrap Rust?

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.

What does unwrap () do rust?

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.


3 Answers

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”)
  • proper error propagation and handling with 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:

  • deliberately distinguish between programming bugs and recoverable errors
  • use a “fail fast” approach for programming bugs

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 ;-)

like image 54
Lukas Kalbertodt Avatar answered Oct 03 '22 01:10

Lukas Kalbertodt


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. Can I say my program is reliable if I use indexing on vectors, arrays or slices?
  2. Can I say my program is reliable if I use integer division?
  3. Can I say my program is reliable if I add numbers?

(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.

like image 39
bluss Avatar answered Oct 03 '22 01:10

bluss


There are two questions folded into one here:

  • is the use of panic! acceptable in production
  • is the use of unwrap acceptable in production

panic! 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).

like image 36
Matthieu M. Avatar answered Oct 02 '22 23:10

Matthieu M.