News for hackers. Stuff that matters.
Re:Where Everything Went Wrong: Error Handling and Error Mes... by brundolf on Friday February 19, 2021 @10:49AM (#26194364)
It's not perfect, but I think Rust's approach is the best one yet.

> Any error system that handles all errors the same way will fail because there are some errors we can ignore, and some errors we must not ignore.

Rust has separate categories for these two things. Panics cannot be handled, which pushes the author to use them sparingly. Results must be handled (or explicitly elevated to panics, in a way that's easy to track down later).

The main weakness of this system, imo, is that the thrower, not the caller, decides whether or not a given error must be handled, and sometimes the answer is "it depends on what the consumer is doing".

That said, the powerful (mainly, no-implicit-null...) type systems that are starting to become prevalent prevent a lot of cases that would have been errors 20 years ago from existing in the first place. In modern typed languages an error is almost always a genuine IO/environment anomaly, which means there are a lot fewer of them to handle in the first place.

Edit: The other big weakness of Rust's system, as mentioned in the article, is the jumble of all the different disjoint Error types. I think the core issue here is that Rust doesn't have ad-hoc union types. I.e. if you want to say "this value is of type X or Y or Z", you have to declare a new enum somewhere, and embed your possible types in each branch, and that's a lot of ceremony at each point where you bubble up a different possible type of error. I understand why Rust's enums are static, though, I wonder if there's a place for some sort of heap-allocated ("dyn?") solution that allows any combination of possible types.

Edit 2: Actually this probably wouldn't be possible because there would be no way to distinguish the union, because Rust carries no type info at runtime. Maybe there could be syntax sugar for anonymous enum types declared inline?

Re:Where Everything Went Wrong: Error Handling and Error Mes... by brazzy on Friday February 19, 2021 @11:32AM (#26194823)
>The main weakness of this system, imo, is that the thrower, not the caller, decides whether or not a given error must be handled, and sometimes the answer is "it depends on what the consumer is doing".

So... exactly the same problem that Java's checked exceptions have?

Re:Where Everything Went Wrong: Error Handling and Error Mes... by masklinn on Friday February 19, 2021 @01:49PM (#26196464)
> So... exactly the same problem that Java's checked exceptions have?

Well yes, but also no because Java's checked exceptions have issues which go way beyond that. Hell I'd say this is not an issue because of the other issues.

In Rust the thrower decides whether the error must be handled, but the default is "yes", and it's the overwhelmingly common decision. Panic is the exception (or multiple APIs are provided). Rust also provides easy way to convert errors to panics, and syntactic sugar to convert between error types.

In Java, the classifications were much less stark, and thus more arbitrary, some exceptions were checked, others were not, but there was little rhythm or reason about it.

Furthermore, there was little to no ability to abstract over checked exception, and the statement-oriented nature of the language made both converting checked to unchecked or converting between different checked exception types a chore.

The verbosity and horrible ergonomics of java's checked exceptions is where the problem always lied, really, with the seemingly arbitrary nature of the classification coming in at third.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by zaphar on Friday February 19, 2021 @02:43PM (#26197173)
Errors are a part of the API. I'm not sure why we keep thinking we need to treat them differently. We don't complain when we have make decisions based on or transform data that is passed to us from a function or method. We just handle the data. Errors are just more data.

The correct thing here is to correctly model your Error domain and map errors you don't control to Errors that you do. In the Java Checked Exception case the Throws clause only grows because everyone is trying to ignore the errors and make somebody else handle it. I would argue that in Rust the explosion of different generic error handling mechanisms is also laziness and a desire to not handle the other half of the API's you are handling.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by danhau on Saturday February 20, 2021 @12:37PM (#26206048)
Thank you. Couldn't agree more.

It seems programmers want errors to handle themselves. It's like they want errors to automagically hook themselves into the entire code base and do "the right thing". Or maybe in other words: programmers mistake errors-as-additional-data, with errors-as-automatic-runtime-behaviour.

Am I crazy?

Re:Where Everything Went Wrong: Error Handling and Error Mes... by dralley on Friday February 19, 2021 @01:47PM (#26196446)
Except more ergonomic and less verbose, usually.
Re:Where Everything Went Wrong: Error Handling and Error Mes... by caffeine on Friday February 19, 2021 @11:40AM (#26194895)
Also, panics can be caught, and now that ppl write a lot of async code, many panics do get caught by default. So it's just a Java RuntimeException now, buried deep inside the dependency tree layers down..
Re:Where Everything Went Wrong: Error Handling and Error Mes... by mlindner on Friday February 19, 2021 @11:01AM (#26194488)
One of the biggest issue with Rust’s panics is there’s many times when you must never panic. For example in an OS, when trying to save your crucial data to disk, in real-time code where panicking would maybe kill someone in the real world, etc
Re:Where Everything Went Wrong: Error Handling and Error Mes... by kibwen on Friday February 19, 2021 @11:45AM (#26194951)
I don't really understand this criticism, because there's no good alternative. Every language is capable of producing invalid states that the programmer did not intend; consider `x / user_input()`. (Unless literally every possible invariant of the program is expressed in the type system, which is not something that we have figured out how to do at scale and not something that even the most type-heavy of the popular languages come anywhere close to.) And once you're in an invalid/unanticipated state, you either crash or you don't. Of the two options, the latter is far worse (On Error Resume Next, anyone?), and is just as likely to lose data/get someone killed.
Re:Where Everything Went Wrong: Error Handling and Error Mes... by masklinn on Friday February 19, 2021 @01:53PM (#26196521)
> I don't really understand this criticism, because there's no good alternative.

There's the option of surfacing the panic-ability of a function in the same way the constness is surfaced, which would allow some subsets of the code to ensure they won't call a possibly-panicing thing, even at the cost of convenience.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by a1369209993 on Friday February 19, 2021 @08:40PM (#26200570)
In the cases where the problem is actually panicing (ie, a non-total function) rather than your choice of core-dumping code, lack of panic-ability is strictly insufficient - `while(1){/*busy-loop*/}` is exactly as bad as `panic();`, and what you actually need is not "must never panic", it's "must make forward progress within some (not necessarily rigorously defined but) small amount of time". That doesn't have anything to do with panic besides that `panic` would be declared as `fn(string)->noreturn[takes_upto: (infinity * seconds)];`, rather than just `fn(string)->noreturn`. (And really, noreturn ought to be enough to infer that on its own.)
Re:Where Everything Went Wrong: Error Handling and Error Mes... by masklinn on Saturday February 20, 2021 @02:50AM (#26202413)
> lack of panic-ability is strictly insufficient

Sure. It is also by far the largest risk you face, so would solve a good 4 or 5 nines if the issue.

> And really, noreturn ought to be enough to infer that on its own.

noreturn only works for functions which never return. Most panicking functions do return, just not all the time.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by a1369209993 on Saturday February 20, 2021 @04:19PM (#26208086)
> noreturn only works for functions which never return. Most panicking functions do return, just not all the time.

Sorry, I meant that any function that was noreturn could be inferred to take up to forever to return (since it always takes forever to return, since it never returns). Then, any function that calls such a function (in a non-DCEed position) can be inferred to also take up to forever to return (even though it might take less than that), because some possible code path through it (it's up to X amount of time to return) takes forever due calling a function that takes forever.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by masklinn on Sunday February 21, 2021 @09:58AM (#26213884)
That is true, although I don't know how helpful that is aside from checking that the annotation is not broken? Much like `const`, the annotation would have to be opt-in either way:

* Rust doesn't infer function types (on purpose)

* like const (or Copy), the removal of the flag is a breaking change, so it should not happen implicitly

So functions would necessarily have to be explicitly marked as e.g. `convergent` or whatever. And while callers of `!`-functions would be easy to check for, I don't think rustc is currently smart enough to infer the totality loops in general.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by a1369209993 on Sunday February 21, 2021 @01:46PM (#26215968)
> Rust doesn't infer function types (on purpose)

> I don't think rustc is currently smart enough to infer the totality loops in general.

Well, yes; we're necessarily talking about changes to the language (or, equivalently, possible different languages).

> while callers of `!`-functions would be easy to check for, [other problems wouldn't]

That was my point, thanks: "when trying to save your crucial data to disk, in real-time code where panicking would maybe kill someone in the real world, etc", checking for the easy-to-check-for problems doesn't actually suffice to prevent disasters.

Re:Where Everything Went Wrong: Error Handling and Error Mes... by mlindner on Friday February 19, 2021 @08:07PM (#26200312)
So you want your kernel to panic whenever it runs out of memory?
Re:Where Everything Went Wrong: Error Handling and Error Mes... by xxpor on Friday February 19, 2021 @09:46PM (#26201017)
Yes, for servers! Hard failures are much easier to deal with than soft-not-dead-but-still-useless states.

echo 1 > proc/sys/vm/panic_on_oom

Re:Where Everything Went Wrong: Error Handling and Error Mes... by mlindner on Sunday February 21, 2021 @08:57PM (#26219820)
Interesting. The company I just left sells a load balancer/application server/do-everything-box and one of the rules of the coding is that it must never crash. Even out of memory errors need to be recovered from and we shut off the kernel feature that makes malloc not actually malloc memory.