Live data from Hacker News

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

msirringhaus.github.io

111–120 of 204 posts

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

#111
post #45

Earlier quoted context omitted.

> Error handling has been wrong since the beginning 100% this. The very concept of "error" is philosophically unsound. There are no errors; only conditions that you dislike. It is unfortunate that programming languages allow to express your emotional detachment to one of both cases of a branch. Nothing good can come from that. I yearn for a language with no error handling nor exceptions. Just plain language construct…

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” (as in correct) variant is the value while the left side is the error, by convention.

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

#112

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…

i’ve use Java C++ and Objective-C and by far the best approach to errors has been Objective-C’s NSError* out parameters.

They're strings. They’re chain-able. They’re visible. They’re hard to ignore. They don’t propagate or crash unless you want them to.

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

#113
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…

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

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

#114

Earlier quoted context omitted.

Code is primarily meant to be read by humans. Humans can't focus on 30 things at once when reading code. A function that should take a list of strings and return a list of all the strings in the first list starting with 'A' will be harder to read if it must also handle allocation errors for the new list, because they are a completely different kind of concern. Even outside of programming, human thought often works ex…

> I am designing my program for a particular use, by definition. No; this is bad engineering. You write a program to conform to a specification. In the specification, it says what must happen when a file does not exist, what must happen when there's not enough memory, etc. Then you write the specified behavior into code.

You're picking nits that don't mean anything. If a specification exists outside of the definition of the program -- aka the code -- then it is only useful as a point of comparison between intent and implementation.

Most software in business is written to implement a process. And business processes are exactly defined as a common workflow with edge cases and exception handling. Not all software is like this, but a hell of a lot of it is. And those business processes are not always complete, and they are typically changing over time as well. Any idea of a global specification outside of the process that the code is actually implementing becomes useless pretty fast.

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

#115
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…

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.

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

#116
post #9

Earlier quoted context omitted.

The only system I think should completely go is Exceptions except in the case of termination or absolutely catastrophic failure - this isn't really about programming but rather that the implementation is a total pain, the compiler struggles to optimize them, and even better they make quite a few safety analyses like borrow checking very difficult because the control flow graph basically explodes when you start consid…

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 logic into the catch and rethrow.

This is a common source of faulty error handling.

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

#117

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…

> Error handling has been wrong since the beginning 100% this. The very concept of "error" is philosophically unsound. There are no errors; only conditions that you dislike. It is unfortunate that programming languages allow to express your emotional detachment to one of both cases of a branch. Nothing good can come from that. I yearn for a language with no error handling nor exceptions. Just plain language construct…

A better word might have been “failure”.

Something is a failure when it fails to do what it says it does.

openfile();

If openfile() does not open a file, then it failed.

This terminology makes it clear we are not just bubbling up errors. It’s the function itself which failed.

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

#118
Python eventually reached a good system, with an official exception hierarchy. Originally, exceptions could be any type. By Python 2.7, new exceptions had to be derived from something already in the official tree. If you catch an exception in the tree, you also catch anything subclassed from it.

You want a hierarchy where there's a subtree for external events, like network and file issues, and a subtree for internal program failures. That lets you catch external events and retry or something.

Python 3.x has a different exception hierarchy, and it's worse. Too much is too close to the root, which leads people to catch "Exception". That catches internal program errors along with network errors, which is not helpful.

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

#119

Earlier quoted context omitted.

The biggest problem with that is that it is very unwieldly if you're using any kind of higher-order functions. To fix that, you need to start supporting error polymorphism. For example, `map` should have a signature like map :: List a -> (a -> b -[err]) -> List b -[err] So that map [1 2 3] +1 //no errors map [1 2 3] sendOnNetwork //returns NetworkError At least, this is one of the major limitation of Java's Checked E…

There've been a few times I've used checked exceptions very locally in Java where I did use a type parameter successfully for this—though it's not really threaded through the type signatures in the standard libraries, so interop can be a problem. Almost like what you wrote, of course more verbosely, something like: interface SomeProcessor { void process(Thing thing) throws E; } void processThingsSomehow( SomeProcesso…

Nice, I never actually tried to do this (my problem was more that I needed to use built-in functions that don't throw, for example sorting a list with a Comparator which can throw).

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

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

There's nothing stopping you from continuing to use anyhow in a library, it just doesn't produce error types with much structure.

You can continue returning `anyhow::Error`, wrap it into a newtype error for your library, or refactor to use thiserror. Thanks to the `?` operator and Rust's type aliases, you can get away with very few code changes to switch between these!

Post reply on HN