Live data from Hacker News

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

msirringhaus.github.io

121–130 of 204 posts

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

#121

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…

zig gets this pretty close to "right" as per your definition.

> Any error system that relies upon developer discipline will fail because errors will be missed.

You must handle all errors on egress to either C abi or a function that does not have an error signature.

> 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. And what's ignorable/retriable to one project is not ignorable/retriable to another.

trys, which are the "lazy" way of error handling (not counting "catch unreachable" - which promotes errors to panics and shouldn't be used except in dev) automatically append the error return code to the trying function's error return call.

> Attempting to get the complete set of error types that any given call may raise is a fool's errand because of the halting problem it eventually invokes. Forcing people to provide such a list results in the Java problem for the same reason.

If every function has a well-defined tree of possible internal "call dependencies" that has finite set of type signatures, you don't have this problem.

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

#122

Earlier quoted context omitted.

In terms of control flow, this: try { someCall(); } catch (ErrorICanHandle err) { //handle } perfectly equivalent to this: err = someCall(); if canHandle(err) { //do something } else { return err } I don't see why so many people think exceptions make code harder to analyze. In my opinion, it is errors themselves that make code hard to analyze, regardless of implementation strategy. The only difference between excepti…

Exceptions are harder to analyze because they create scopes from which you are already "handling" errors, but if you add code to the scope that throws a new error of that type your handler may not actually be able to handle it. try { someCall(); someOtherCall(); } catch (ErrorType err) { //handle } Which method throws? If they both throw ErrorType but one of them cannot be handled, then you have to put additional log…

Yes, this is an easier mistake to make with exceptions than with error values. Still, the fix is to do exactly the same thing as in the error values case:

  try {
    someCall()
  } catch (ErrorType err) {
    //handle one way
  }
  try {
    someOtherCall()
  } catch (ErrorType err) {
    //handle another way
  }
It's no less verbose than the error values way, though again, it is easier to make the mistake in the first place.

I'm not trying to claim that exceptions are ultimately better than error values, just that the difference isn't so much "non-local control flow" as it is explicitness vs implicitness. Exceptions are implicit, error codes are explicit. Both have benefits and drawbacks.

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

#123
post #111
post #45

Earlier quoted context omitted.

Isn't that what the Result type on Rust is? Sure, one of the branches is still called Error but it's just a plain language construct (a sum type you can write yourself).

The Result type is specifically designed to store value-or-error. One may use it diffently but that’s what it’s made for. The library designers had a choice between making a generic this-or-that type or a value-or-error type and they chose the latter because they thought that that would be the common this-or-that use-case. Even Haskell’s more generic-sounding “Either” type is made for the same purpose: the “right” (a…

I don't understand what the problem is, in that case. Is it just the fact that it's called an Error instead of something more generic? Not trying to sound dismissive, just trying to understand if there's something I'm missing.

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

#124
post #51

Earlier quoted context omitted.

In terms of control flow, this: try { someCall(); } catch (ErrorICanHandle err) { //handle } perfectly equivalent to this: err = someCall(); if canHandle(err) { //do something } else { return err } I don't see why so many people think exceptions make code harder to analyze. In my opinion, it is errors themselves that make code hard to analyze, regardless of implementation strategy. The only difference between excepti…

this issue is knowing whether you are catching the right errors

You have the same problem with error codes, don't you?

The bigger difference is knowing whether you should expect errors at all - with exceptions, you can forget to handle an error and you may screw up an important assumption, like not unlocking a Mutex if an Exception is raised. With error codes, you have the opposite problem: you may forget to check for an error, and continue to execute in a bad state.

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

#125
post #113
post #105

Earlier quoted context omitted.

> Contrast that with Java, where any uncaught or logged exception prints a detailed stack trace and (usually) one or more useful error messages, by default, since day one. I think you're misunderstanding what the OP is trying to show here. If all you want is a backtrace, then Rust supports that out of the box. Here's an approximation of the full error message that you would see from the first example in the post: thr…

I don’t get why this is off by default for debug builds though. Maybe that’s a separate concern.

In my experience enabling the backtrace is necessary in very few cases. I've defined a bash alias to save me a little typing in those situations. As it can be turned on persistently by setting a single environment variable I'd say it's a good middle ground.

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

#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.

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

#127
post #105
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…

> Contrast that with Java, where any uncaught or logged exception prints a detailed stack trace and (usually) one or more useful error messages, by default, since day one. I think you're misunderstanding what the OP is trying to show here. If all you want is a backtrace, then Rust supports that out of the box. Here's an approximation of the full error message that you would see from the first example in the post: thr…

In other compiled languages, stack traces can be supported by looking up code references on the stack in a map of executable addresses to source code locations.

Even in the absence of stack frames, the mere contents of the stack with lookups where possible is really useful, and usually more than enough.

The cost is a little code to do lookups at runtime, and making the mapping data available (typically compressed and embedded in the executable).

(Note that this isn't stack unwinding or exceptions. This is backtraces. Rust, like most languages which embed errors in return values, makes the developer do the stack unwinding manually.)

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

#128
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.

That's Go. IMO Rust's approach is vastly saner, since a Result type has to be explicitly handled one way or an other. You simply can't access the returned value without unwrapping it.

Of course that leaves function that can fail but don't return any value, but since Result is tagged "must_use" you get a compiler warning if you don't explicitly discard the result with something like `let _ = foo()`.

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

#129
post #111

Earlier quoted context omitted.

The Result type is specifically designed to store value-or-error. One may use it diffently but that’s what it’s made for. The library designers had a choice between making a generic this-or-that type or a value-or-error type and they chose the latter because they thought that that would be the common this-or-that use-case. Even Haskell’s more generic-sounding “Either” type is made for the same purpose: the “right” (a…

I don't understand what the problem is, in that case. Is it just the fact that it's called an Error instead of something more generic? Not trying to sound dismissive, just trying to understand if there's something I'm missing.

The problem? You would have to ask the poster that you initially replied to.

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

#130

Earlier quoted context omitted.

> throw/raise is GOTO. Worse, it's COME FROM! https://en.wikipedia.org/wiki/COMEFROM

To be fair, throw is GOTO. Catch() is COMEFROM :).

Technically, `throw` is `goto somewhere`.
Post reply on HN