Live data from Hacker News

Where Everything Went Wrong: Error Handling and Error Messages in Rust (2020)

msirringhaus.github.io

141–150 of 204 posts

Re: Where Everything Went Wrong: Error Handling and Error Messages in Rust (2020)

#141
post #96

Earlier quoted context omitted.

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 t…

>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?

> 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 Messages in Rust (2020)

#142
post #99

Earlier quoted context omitted.

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

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 l…

> 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 Messages in Rust (2020)

#143

Earlier quoted context omitted.

Maybe CL got this right with the condition/restart system? In addition to their utility for experimental programming, conditions work really well for handling errors programatically. The general problem seems to be something weird happening far down in the system, for which the correct way forward is dependent on how we got there. This situation can't be handled where the issue happened since that would break encapsu…

I agree, for the actual handling part, I think CL's error system is the best I've seen, and I'm surprised that more languages don't implement a similar system. It doesn't solve the issue of forcing programmers to handle errors..but then, CL doesn't care a lot about hand holding.

One advantage of condition systems is that the caller gets to decide whether the condition is even an error. Though the restarts are still under the control of the callee.

Re: Where Everything Went Wrong: Error Handling and Error Messages in Rust (2020)

#144
post #126

Error handling has been wrong since the beginning, and has continued to be wrong ever since. First, we had error codes. Except these were wrong because people forget all the time to check them. Then we had exceptions, which solved the problem of people forgetting to check by crashing the app. Then the Java team got the bright idea to have checked exceptions, which at first helped to mitigate crashes from uncaught exc…

> Any error system that relies upon developer discipline will fail because errors will be missed. Haven't there been some languages that force functions to return some kind of tuple like: result,error And forces the programmer to at least do: if(error) { } It does not force any kind of correct handling, but simply oversights should be caught. I might be imagining things though.

What you're describing is Go, except its requirements are much weaker than that.

Rust, meanwhile, is way stricter than that, and has gone way further on the "not relying on developer discipline" path: a failing Rust function will return a `Result`. You can't even access the value without explicitly checking whether it's a value or an error one way or the other, which you can very much do so in e.g. Go. Rust also has an attribute called `must_use`, which can be set on types and functions. That attribute causes the compiler to emit a warning if the marked object is not used at all (either the type or the function's result), so while go will not say anything if you write

    Foo()
and that returns an error (or an error and a result you happened not to care for), Rust will absolutely complain by default in the same case, you will need to write at the very least

    let _ = Foo();
Go has a second issue, which is that in

    result, error := Foo()
that you "have to" handle the error is a consequence of the unused variable check (can't define a variable and never read from it). However because it's that instead of something dedicated, this:

    id, error := Foo()
    result, error := Bar(id)
    if error != nil {}
will work fine, with no complaint. Despite possibly passing complete nonsense to Bar if Foo is in error. Also works with

    id, error := Foo()
    if error != nil {}
    result, error := Bar(id)
Funnily enough Rust will also warn in both those cases, because it tries to track individual writes.

Re: Where Everything Went Wrong: Error Handling and Error Messages in Rust (2020)

#145
post #115

Earlier quoted context omitted.

I'd like to just point out that Java didn't have exception chaining until 1.4. And suppressed exceptions were added in 1.7 along with the try-with-resources construct. I know 1.4 was a long time ago, but it was 4 years after 1.2, which is what I typically consider the start of Java becoming a dominant language. I'm only posting this because I find a lot of people forgetting that Java has had a very long history at th…

Oh, I've been around since the 1.2 days as well. Somehow, exception chaining didn't register as a big change when it happened. But yeah, incremental improvements have been pretty important as well.

I agree regarding the chaining. I don't even remember thinking twice about it when it was introduced. But the ability to transform exception types without losing the original context and stack trace is actually a pretty big deal!

Re: Where Everything Went Wrong: Error Handling and Error Messages in Rust (2020)

#146
post #101
post #16

The rule of thumb for me is `thiserror` for libraries, `anyhow` for executables. Seems to work well enough in the vast majority of cases. I do agree that the Rust way can be frustrating at first. But then, at some point it becomes clear that being forced to keep your error conditions in mind at all times is actually a healthy thing. Then going back to languages where code may fail anywhere seems less than optimal. So…

> The rule of thumb for me is `thiserror` for libraries, `anyhow` for executables. And as the executable gets larger and refactored, some of the "executable" code will inevitably become "library" code. What then?

> And as the executable gets larger and refactored, some of the "executable" code will inevitably become "library" code. What then?

Then that is migrated to `thiserror` (or something bespoke) at the same time as it's migrated to the library context.

The library / executable is the delineation between providing a Rust-level API for third parties versus consuming such APIs.

If you're providing a Rust API, you want to provide precise error so that the user is able to precisely target and handle errors if they need to.

If you're only consuming Rust APIs, then you want to precisely handle some of the errors you get, and just chuck the rest over the side.

Re: Where Everything Went Wrong: Error Handling and Error Messages in Rust (2020)

#147
post #110
post #95

Earlier quoted context omitted.

The article isn't really about any of that though. It's about a much simpler problem: how to produce error output that is useful to developers looking to fix a bug. And I'm no Rust developer, but it looks to me like it basically demonstrates how Rust is an abject failure in that regard. The developer has to jump through lots of nonobvious hoops and choose between competing libraries to get anything useful. Contrast t…

It took Java until Java SE 14 (last year) to produce NPE stacktraces which actually tell you which variable was null. At least in Rust third parties could have created a library to remedy a similar situation.

For whatever reason, it look a long time for parameters and locals to have a name in addition to a slot and a type in the JVM bytecode. Before that data was introduced, it was an impossible task. Also why things like de-/serialization frameworks required annotations on parameters duplicating the parameter name.

Re: Where Everything Went Wrong: Error Handling and Error Messages in Rust (2020)

#148
post #40
post #16

The rule of thumb for me is `thiserror` for libraries, `anyhow` for executables. Seems to work well enough in the vast majority of cases. I do agree that the Rust way can be frustrating at first. But then, at some point it becomes clear that being forced to keep your error conditions in mind at all times is actually a healthy thing. Then going back to languages where code may fail anywhere seems less than optimal. So…

Why not `thiserror` for executables as well? It happened to me a few times that I started to write an executable program, but then realized I want to embed its functionality in a library. Converting from `anyhow` to `thiserror` at that stage would be extra work that can be avoided.

> Why not `thiserror` for executables as well?

You can absolutely do that if you want, it's just that usually when writing an executable you don't care about creating the precise error types thiserror provides (especially doing so executable-wide), you'd handle the errors you get from reqwest or sqlx or whatever when you get them, and those you don't handle you just want to bubble up to an executable-wide handler.

Re: Where Everything Went Wrong: Error Handling and Error Messages in Rust (2020)

#149

Error handling has been wrong since the beginning, and has continued to be wrong ever since. First, we had error codes. Except these were wrong because people forget all the time to check them. Then we had exceptions, which solved the problem of people forgetting to check by crashing the app. Then the Java team got the bright idea to have checked exceptions, which at first helped to mitigate crashes from uncaught exc…

> It's a hard problem, which is why no one has solved it yet.

On the contrary, the Common Lisp condition/restart system solves it, and it's maddening that this hasn't been adopted anywhere else.

An exceptional state signals a condition, and without unwinding the stack, looks for a handler for that condition, which can, among other things, restart the computation from a lower stack frame. In development mode, it defaults to dropping into the debugger/repl; in release mode, an unhandled condition panics.

Re: Where Everything Went Wrong: Error Handling and Error Messages in Rust (2020)

#150
post #96

Earlier quoted context omitted.

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 t…

>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?

[deleted]
Post reply on HN