This, so much.
I'll add that witch-hunting .expect()
s and/or .unwrap()
s without witch-hunting unreachable!()
s, panic!()
s, and assert!
s is just contradictory: all of these can and should be used to make the code guard against unrecoverable situation: it's really supposed to represent a fatal error.
Some people, in some situations, may dislike the idea of unwinding altogether, since it could jeopardize the whole thread (if panic = unwind
) or process (if panic = abort
) altogether. In the former case, catch_unwind
is there to help with that (although it could lead to memory leaks), and in the latter case, or if memory leaks are unacceptable, using subprocesses is then the more general solution to contain these disastrous / fatal situations.
-
So, in that context, trying to avoid the whole unwinding mechanism could be desirable (
no panic
guarantee): that would mean that all your APIs would be made-> Result
-fallible, and that you'd have aFatalInternalError
variant for the error case:macro_rules! bail {( $($tt:tt)* ) => ({ error!($($tt)*); return ::core::result::Result::Err($crate::FatalInternalError); })} // etc. // and for unwrap/expect: let value = option.ok_or(FatalInternalError)?; let value = option.ok_or_else(|| bail!("Thing should be in here!"))?;
But, imho, doing this pervasively / systematically would really be like lifting a finger at the whole panic / unwinding mechanism (which some people may be genuinely tempted to do ): the language was made with panics for a reason, so it's legitimate to use them as well (again, though: this should still be done sparingly, only for assertions / logic bugs / something that in a perfect program would be genuinely unreachable).
A practical example
Proc-macro code is famous for not really having "a Result
-based API baked in", which may have lead people to use panic!
as a way to error on invalid input. But this is not good practice / good style, since the error message becomes ominous (proc-macro panicked
), and the error location, awful (it just blames the macro itself, not the problematic part of the input).
With libraries such as ::syn
, one can get back to a Result
-based API, with an unwrapping in the very final layer of .unwrap_or_else(|err| err.into_compile_error().into())
.
Does that mean you should never panic in proc-macro code? No! Again, for logic bugs / supposedly unreachable / crazy code paths, I think panicking would be in order: should there be a bug and that panic be reachable, the "ominous" proc-macro panicked
would be warranted, since it would clearly convey there being a bug in the proc-macro code, not a problem with the input the caller gave it.
- An example when using
syn
is that if you.peek()
ed a certain token, you ought then to follow-up with.parse().unwrap()
, rather than.parse()?
: fail loudly on logic bugs!
This is similar to nice compiler errors on incorrect input, versus fatal compiler bugs on mishandled input: ICEs (Internal Compiler Error), which stand for the compiler itself panicking rather than erroring.
So I personally find the distinction to be useful, and for these situations, the built-in unwinding mechanism to be quite convenient