I would suggest reading BurntSushi on Error Handling in Rust and the same author on Using unwrap() in Rust is Okay.
The core of these answers to the question (which I agree with, FWIW) is that a panic, including one from unwrap()
or expect()
, is a sign that the program has a bug and needs a programmer to debug it. If the problem can be resolved by the caller (which, in some cases, will be the end user of the program, and not another programmer), then it's "return an error" time, using Result
or Option
as appropriate.
In this model, unwrap
, expect
, panic!
, unreachable!
, todo!
and all the other myriad ways to induce a panic (e.g. array[idx]
) are markers for the human reader; they tell you that while this state cannot be proven impossible via the type system (and thus checked by the compiler), the author believes that this state cannot plausibly happen, and if it does, entering this state means that you need to debug the program.
For the specific case of Mutex::lock()
, an error is returned if a thread panicked while holding the lock. There are ways to recover from this, where you re-initialize the contained value (see the example for Mutex::clear_poison
) or replace it with a new lock and drop the poisoned lock completely, so there's a case for it being an error.
But if you are asserting that your threads never panic while holding this lock (absent an unknown bug in the program), then lock().expect("poison")
is perfectly reasonable; you are saying that lock()
can return an error, and if it does, it indicates that the program is in a state that you, as the programmer, believed was impossible. This is a great time to panic - you need a Rust programmer to dive in and find out what has happened to get the program into a state that the original programmer of this code believed was impossible, and fix it.
TL;DR: panic for cases where the only way to proceed is to get a Rust programmer to fix it, error where it's plausible that the caller can do something sensible.