可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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.
回答1:
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 ;-)
回答2:
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:
- Can I say my program is reliable if I use indexing on vectors, arrays or slices?
- Can I say my program is reliable if I use integer division?
- 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.
回答3:
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).
回答4:
unwrap()
is not necessarily dangerous. Just like with unreachable!()
there are cases where you can be sure some condition will not be triggered.
Functions returning Option
or Result
are sometimes just suited to a wider range of conditions, but due to how your program is structured those cases might never occur.
For example: when you create an iterator from a Vec
tor you buid yourself, you know its exact length and can be sure how long invoking next()
on it returns a Some<T>
(and you can safely unwrap()
it).
回答5:
unwrap is great for prototyping, but not safe for production. Once you are done with your initial design you go back and replace unwrap()
with Result<Value, ErrorType>
.