5 ms·
Implicit exception propagation (like C++, Java, JavaScript, etc. do) is a very bad design because when a statement can throw an exception, you need to design yo
by devit 6y ago
Implicit exception propagation (like C++, Java, JavaScript, etc. do) is a very bad design because when a statement can throw an exception, you need to design your code to rollback any changes before it, so it's essential that you know at a glance which statements can throw exceptions (in Rust, the ones with a "?").
As for implementation using unwinding vs a sum type, they are semantically equivalent, but using a sum type like Rust does allows you to use exceptions even when the error case is common without the steep performance degradation that happens with unwinding, at the expense of a very slight slowdown in the no-errors case.
Rust also supports unwinding/aborting exceptions as "panics". These are implicit because all accessible mutable memory is destroyed by a panic, since you must not keep references across catch_panics, so there is no need to rollback (except for unsafe code, in which case you need to assume that all function calls can panic).
- golergka 6y ago> Implicit exception propagation (like Java I haven't touched Java in 10 years, but didn't it have explicit propagation with `throws` statement for this exact purpose?
- bluejekyll 6y agoThere’s always the feared NumberFormatException which is unchecked, for some crazy reason. Similarly, all unchecked errors and exceptions mean that you never know. So you have to treat critical sections of code as areas where resources need to be cleaned up by using try and finally blocks.
- masklinn 6y agoJava has both checked exceptions (explicit `throws`) and unchecked (no explicit notification), and its checked exceptions are often avoided because the delineation is not very good and they're cumbersome to work with.
- devit 6y agoI mean the fact that you in Java, etc. any function you call may throw an exception, which automatically propagates upwards from the code that calls the function, which results in part of the code in your function being executed and part not being executed, without any obvious signs of that (like having an early return or a "?" in the function). If the exception is not a RuntimeException and not declared in the throws clause, then the compiler will error out, but in other cases the code will compile. In Rust, you need to use the "?" operator when you call an exception-throwing function to propagate the exception, and if you don't you will get a Result, and a warning if you don't use it (i.e. not using "?" in Rust is like using Try in Scala). If Rust code has no "?" or "return", then no exceptions will be thrown and the code will fully execute, except for aborts and panics which are guaranteed to make all visible memory inaccessible, so you will never see a partial modification of an object due to an unexpected exception being thrown.
- mmebane 6y agoHow common is the ? operator in modern Rust code? I feel like if you reach a point where almost everything returns Result and almost every call is qualified with a ?, the pattern has turned into something that's not much different from checked exceptions.
- dralley 6y agoIt is pretty common, but Rust gives you a lot of tools to handle errors in-place more easily than other languages as well. I'd still consider it a significant improvement over checked exceptions in the vast majority of situations.
- WesolyKubeczek 6y ago> you need to design your code to rollback any changes before it Citation needed on this one. In almost two decades I never once came across code that straight away required such rollbacks.
- setr 6y agoIts the natural control flow for try-catch exceptions: try it out, and if it fails, clean up and move on That cleanup phase is generally some kind of rollback, usually in the form of discarding whatever partially constructed datastructures (sometimes in the form of a savepoint and reverting, but thats rarer ime). The other type of catch handling is to just force everything into some sane default state, to avoid having to care what specifically borked
- dllthomas 6y agoI come across this all the time; usually it can be handled well with RAII or similar, in languages with the prerequisites. Consider a simple case where I am trying to receive data over a socket and write it to a file. I need to 1) open the socket, and 2) open the file. Whichever order I do these in, if the second one fails I need to be sure I clean up the former.
- WesolyKubeczek 6y agoHow can you be sure your cleanups worked? Use a protocol that can cope with a disconnect well. Write to a temporary file which either you then rename, or the OS discards if there was a failure. Trying to “clean up” manually can break things further since you don’t and can’t account for all possible reasons for a failure. A simple example: your hard drive is failing, and performing any kinds of cleanups on it will get your program stuck even further. Also, does it really matter too much for your stated purpose if you get an ENOSPC, an EPERM, or an EIO?
- dllthomas 6y agoYou're over-complicating it. The cleanup I was describing is freeing resources that had been allocated to the process before the error. Sure, in a sufficiently crazy situation, close() might not actually close the file descriptor - maybe I've somehow trampled on the memory associated with the function (having worked around any kind of W^X), or maybe the operating system is just seriously fucked - but it seems crazy to expect that on the basis of failing to open a file. You should still close the socket.