7 ms·
It'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
by justinpombrio 4y ago
It'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.
- deleted 4y ago[deleted]
- nindalf 4y ago> seems like Rust people are from another planet. There’s no need to talk in a condescending manner. What they said was correct - an “unwrap” outside of test/prototyping is considered a serious bug. They Rust loving strawmen you’re creating never claimed that every line of Rust ever written is perfect and bug free.
- Nathanba 4y agoSo you essentially want me to write error handling code nonstop, constantly, all across my functions. Practically after every 5 lines of code there is going to be an unwrap() where I'm not allowed to call unwrap() so I have to know the details of the implementation, the error code, deal with the error code, return early from the function and then gracefully handle it all. Meanwhile in a language that has exceptions I just put a try catch around all the code I think works fine but maybe not and I deal with it in a single location in a way where I dont have to care about what the precise error code might be. Error code programming really seems to be objectively worse for everyone except the compiler writer. Somehow people let themselves get convinced that this is better when it's objectively not.
- Gwypaas 4y agoOr simply use Thiserror together with bubbling. https://crates.io/crates/thiserror/1.0.24 https://crates.io/crates/thiserror/1.0.24
- svnpenn 4y agoSorry, but if the language cant even get errors right, I'm certainly not going to download a package to do it.
- nindalf 4y ago? Are you mistaking Rust for some other language? Error handling in Rust is mostly just using the `?` operator. The Rust book explains - https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator https://doc.rust-lang.org/book/ch09-02-recoverable-errors-wi...
- varajelle 4y agoUnwrap is supposed to be used when the developer know that the error can't happen. Or that if that error happen there is no recovery anyway, and the best thing to do is to abort the program.
- simion314 4y ago>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: Reality is not that simple, if you worked in this industry you would know. For example I was building a web scraper years ago and the WebView would crash since is C/C++ , instead of doing it's job and show a web page or a broken web page it crashed my entire program,. The solution was to split my program in a parent program and a child program so this bug does not bring my entire thing down, and I can crash the issue and record the bad url that crashes and try again or just skip it. I would hate to use Rust libraries that would crash my entire program if they for some reason are bugged. In my experience I found bugs in many popular libraries. So in Rust if I import a say library to resize an img and say the img is corrupted and library is shit it will crash my entire program? I would prefer a higher language where I can try=crash the image resize function and if shit goes wrong I can show the user a relevant message , or fallback to some other resizing method.
- notriddle 4y agoYou need to run it in a separate process. Rust does not have good enough fault isolation features to safely assume a buggy image processor won’t break your app. * Entering an infinite loop can bring down everything. A separate thread might not, but since Rust provides no way to kill a thread without it cooperating, there is no way to stop a stuck thread without bringing down the whole process. * Stack overflow is an instant abort, not a panic. * Double panic, where panicking calls a destructor that itself panics, is an instant abort.
- simion314 4y agoQuestion, if you are a library/program author why would you intentionally use a panic and not cleanup and return an error? Maybe I misunderstood and in fact good developers never trigger panics unless there is no way to avoid it, like if they could not prevent it with more checks or is it impossible to cleanup because they already fucked up, wrote garbage in the process memory and safest thing is to kill the process.
- 4y ago
- jcelerier 4y agoI don't understand your #2 (or your whole point). It's exactly the case for exceptions and how exceptions happen. "You won't get an exception if your code is written correctly and the inputs of your program match your programmer expectations" yeah maybe two year down the line someone refactors the code which was resizing the vector before and now you have the most run-of-the-mill exception ; I just hope that someone making an app with your library has a way to catch the panic so that the software doesn't crash but shows a helpful error dialog to your user and makes a backup of its data before softly existing instead, otherwise we're really back at the pre-1980 state of the art of software design
- Hackbraten 4y ago> maybe two year down the line someone refactors the code which was resizing the vector before and now you have the most run-of-the-mill exception In other words: there’s a bug in the code, and that bug has now caused an unrecoverable error, panicking the thread. Now the thread has died (or maybe caught the panic to present a friendly error message). Either way, the user is now aware of the bug, and disaster has been avoided. Of all situations where your app might want to create a backup of its state, why would you choose to do so precisely while unwinding a crashed thread, where all assumptions, bets and invariants are already off? And what would the helpful error dialog even say? „A problem has occurred and the app will now shut down“? From the user’s point of view, is that really an actionable or helpful error dialog?
- jcelerier 4y ago> And what would the helpful error dialog even say? „A problem has occurred and the app will now shut down“? From the user’s point of view, is that really an actionable or helpful error dialog? Yes, literally. This is already much better than anything that gets the spinning ball of death of macOS going. You can even continue if you are running an event-driven app where the error may have happened as part of an event handler (and thus limited to a very specific part of the software). To give my own experience: I develop https://ossia.io https://ossia.io and use this catch-all method. In 7 years I've gotten numerous thanks from the users for it not crashing but being able to carry forward in case some issue crops up in a sub-sub-sub-module. Not a single time I remember this to cause some memory corruption later. (backing up state is done up to the previous user action but while in my case it works, it's not always practical)
- bjarneh 4y agoWasn't this argument used all the time by the Go community. I.e. only use panic when you intend the program to halt, and handle all potential problems with the Error type. I think Rob Pike even said it's easy to see where a program fail in one of his talks? But to me the superb thing about exceptions, is that error handling can be done where it makes sense. I.e. we can try{ problem-code }catch(problem){ handle problem } in a single location. Otherwise we end up peppering the entire code base with a ton of error checking far down the call stack, where we really cannot do much about the problem anyway (unless we are writing command line tools where error handling is just writing the problem to stderr). Exceptions gives us a nice way to let problems bubble up to the surface, while also stating what the problem was, and where it occurred. That is great IMO.
- oconnor663 4y ago> Exceptions gives us a nice way to let problems bubble up to the surface, while also stating what the problem was, and where it occurred. Work is ongoing on some of this, but there are popular libraries in Rust (like `anyhow`) that let you attach backtraces to regular errors, add context, etc. Propagating erros to callers is handled with the standard `?` operator, which means "short-circuit and return this if it was an error, otherwise give me the successful result". This has the benefit of making early exits explicit, without interrupting the visual flow of straight-line code.
- bjarneh 4y agoOK, I'm not into Rust. It just seems a bit too complicated :-)
- pornel 4y agoThe simple explanation is that Rust has an equivalent of Java-style exceptions of "throw here, handle elsewhere", but has a different syntax for this. Instead of try/catch, there's a `?` operator to return ("rethrow") the error to an outer scope. It's a better fit for Rust's use of a generic Result type, but overall its usage is similar to the checked exceptions in Java. Because Rust uses the type-safe explicit Result/? approach for all non-bug failures in the program, the implicit panic (that behaves similarly to RuntimeException in Java) is reserved for assertion failures and crashes only. `catch_unwind` is not guaranteed to work in Rust. There's a setting to disable it and always hard abort() the whole process on every panic. Rust is serious with panics being for programmer's bugs only, and not trivialities like "file not found".
- edflsafoiewq 4y agoPanics are exactly like exceptions in the fact the entire standard library pays the cost of being exception safe.
- roblabla 4y agoYou can turn panics into aborts with `panic = "abort"` in Cargo.toml, in which case nobody has to pay for being exception safe (though, to get the full benefits of this, you may have to rebuild the stdlib? I'm not entirely sure here).
- edflsafoiewq 4y agoI'm talking about the cost paid by the library author for the additional burden of writing exception safe code. Whether you use this downstream doesn't matter, the cost is already paid (in fact arguments like "no one catches" make it worse since the cost is paid and no one benefits).
- styluss 4y agoThe only place I've seen people using catch unwind was in Sentry library to catch panics. Needless to say that it was never used even before we removed all unwraps from the code.