8 ms·
> It may sound stupid, but you can't have unhandled exceptions if you don't have exceptions... > panic!() exists in Rust, but that's not how recoverable errors
by ChiefOBrien 4y ago
> It may sound stupid, but you can't have unhandled exceptions if you don't have exceptions...
> panic!() exists in Rust, but that's not how recoverable errors are handled.
This is the worst argument in the whole article, and this is the worst part of the language. Everyone says it's not like exceptions, but in fact it is much worse. Panic is stringly typed and you can catch_unwind it, just like with try/catch in any other language. And the actual worst part of it, you will never know if a panic can occur in any of the underlying functions until it is too late. Developers be damned if they want to choose different behaviour other than crashing the whole program.
Either double down on using the standard error handling everywhere, or put something like "throws panic" in the function signature (ala Java checked exceptions). Many parts of the language has strict checks for everything, why does panic has to be an outlier?
- oxff 4y agopanic! and unwrap are more like assertions about program invariants, but of course this can be abused. at least the linter will yell at you if you have a panic but not documented it.
- justinpombrio 4y agoIt's not like exceptions because it's not used like exceptions. You only use panic if you want to crash the whole program. If you don't want to crash the whole program, you don't use panic. You do not want to crash the whole program if the user data failed to validate, so you do not panic in that case. If a library panics on invalid user data, that's a pretty serious bug. I've been programming in Rust since it came out, and a couple of those years professionally, and I don't think I've ever seen anyone use catch_unwind. Maybe once in a test case? To be concrete, let's talk about an example of a panic. Say you want to access the 3rd element of a vector. There are two cases: 1. You're not sure whether the vector actually has three elements on not. In this case, you call `my_vector.get(2)`, which returns an Option, and you handle the case where it's present and the case where it's not. This is standard error handling. 2. You are sure that the vector has at least three elements. Perhaps you just checked its length for some other reason, or you are careful to maintain this invariant, or you just constructed this vector by pushing 5 elements onto it. In this case, you would typically use `my_vector[2]`, which panics if the vector is too short. For #2, the thing to notice is that this function literally never panics, under any input whatsoever if it is written correctly. Should that fact really clutter up its type signature, either by forcing it to return a Result type or by forcing it to have a "throws panic" marker? EDIT: This is for a function that uses a possibly-panicking operation, `my_vector[2]`. There are also the functions that define a potentially panicking operation, like the vector indexing function itself. You could put a marker in the type signature of those, that would be reasonable. Though it would only be for users; the compiler wouldn't care.
- svnpenn 4y ago> If a library panics on invalid user data, that's a pretty serious bug. I swear, sometimes it seems like Rust people are from another planet. What do you think "unwrap" does? It's not used in every library, but certainly in many of them.
- justinpombrio 4y ago> What do you think "unwrap" does? It panics just like my `my_vector[2]` example does. What did you think `my_vector[2]` did? Libraries use `my_vector[2]` too. I don't get why we're changing topics from one commonly used panicking operation to another.
- deleted 4y ago[deleted]
- nulld3v 4y agoThe assertion remains true though. Unwrap should only be used if you are prototyping or you are 100% sure it will never actually panic. It's just like the IndexOutOfBounds exception in Java. Many functions can theoretically throw it, but most libraries and programs do not catch it because usually if it is thrown it means that something happened that the programmer did not expect and therefore the program should crash.
- elbear 4y agoThere's a difference between what you should do and what people actually do. If a lot of them use unwrap in production, then OP's argument is valid.
- nulld3v 4y agoThe problem would not be that it is commonly used, the problem would be that it is abused. And I don't see that happening currently. The assertion that "If a library panics on user data, that's a pretty serious bug" remains true. If a library is panicking on invalid user data, it is because they are abusing panic, which is a serious bug. Or they just didn't realize that their code could panic, which is also a serious bug.
- touisteur 4y agoTo be a bit fair, checked exceptions in java also have their 'bypass' system, since Errors are not checked. So you can't be sure whether someone will decide to throw an error in the middle of library code. You still have to catch-all. I'm not saying it's better. I haven't seen a way to do exceptions better than fully-checked exceptions, but you have to be ready to have buffer/integer over/underflow exceptions everywhere or have a fine prover for the absence or runtime erroes to 'allow' you not to have them in your signature. Otherwise having discriminated records (or option types if you prefer) for return and error-handling seems more down to earth, if a bit painful to write.
- lenkite 4y agoFrankly, I love Java's checked and un-checked exceptions differentiation even if the standard library is confused about it. Make logical exceptions (depending on purpose of interface) into checked-exceptions. Make system exceptions into un-checked exceptions. Document in javadoc with `@throws` A higher level module can wrap and re-throw into the appropriate exception if needed. Error handling can be done in the desired place instead of scattered across the code.
- kaba0 4y agoYeah, I also believe that Java is the closest to the best error handling I am aware of. Unfortunately though, it is inheritance based which is a bummer here. It would be perfect with sum types though.
- the_mitsuhiko 4y ago> Everyone says it's not like exceptions, but in fact it is much worse. Panic is stringly typed and you can catch_unwind it I'm not sure which argument you are trying to make but panics are not stringly typed unless you panic with a string. You can use panic_any(MyPayload) and then it panics with that instead.
- orra 4y agoTo be fair to catch_panic, it exists for a very specific purpose: to prevent the undefined behaviour of unwinding across an FFI boundary.
- caffeine 4y agoI just wish there was an ergonomic way of saying “Please check if the following code can possibly panic, and fail to compile if it can.” That would allow critical sections that happen to use a library not to need to audit all the code in the library for panics.
- slavak 4y agoYou might be interested in Prusti: https://github.com/viperproject/prusti-dev https://github.com/viperproject/prusti-dev