Live data from Hacker News

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

msirringhaus.github.io

91–100 of 204 posts

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

#91

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…

Nice summary. Ideally, I would like languages just to do simple exceptions (no checked stuff) and create a way to catch them.

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

#92

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.

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

I've never, ever seen a specification which mentions file-not-found or out-of-memory errors. The "specifications" I get from my clients are more like (paraphrased):

> Listen, adwn, I have all those files in a shitty, undocumented file format designed by another group in another building, could you please add an import button for them? Oh, and can I have it by Friday? Thanks, you're the best!

I'm exaggerating only slightly. And then I go and try to make sense of what they need, and I implement error handling on top of it.

Your specifications tell you exactly how to deal with file-not-found situations? You lucky bastard ;-)

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

#93

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 make code harder to analyze because they implement non-local control flow. Your two example statements aren't really equivalent. `ErrorICanHandle` may be a kind of exception that the `someCall` function has no idea about whatsoever -- it could be an exception propagated from much farther down the call stack -- while your if statement assumes the error was explicitly returned from the function.

In an exception-based language, assuming all functions potentially throw, code like this:

  someCall()
is equivalent to code in a language without exceptions that looks like this:

  err = someCall()
  if err {
    return err
  }
(ignoring for a moment non-error returns, which don't change the point significantly)

Having functions that are guaranteed not to throw / return errors doesn't significantly change this shape.

So basically it's no more non-local than the code you'll end up writing with error codes, in most cases, any way. The only difference is implicit vs explicit.

> `ErrorICanHandle` may be a kind of exception that the `someCall` function has no idea about whatsoever -- it could be an exception propagated from much farther down the call stack -- while your if statement assumes the error was explicitly returned from the function.

someCall doesn't need to know about the meaning of the error in either case. It only needs to propagate any error it receives from any functions it calls (that it can't handle itself). Think about the function I wrote myself: did it need to know the precise type of err? Nope - it just needed to know that it implements some kind of Error interface (which may be as simple as being a negative number, like a POSIX error code).

Edit to give a more complete example:

  extern void bar();
  void foo() {
    bar()
    doOtherStuff()
  }
  void someCall() {
    foo()
    doMoreStuff()
  }
  void main() {
    try {
      someCall()
      doFinalStuff()
    } catch (BarSpecificException e) {
      doErrorStuff()
    }
  }
Is equivalent to this code:

  extern err bar()
  err foo() {
    err = bar()
    if err {
      return err
    }
    err = doOtherStuff()
    if err {
      return err
    }
  }
  void someCall() {
    err = foo()
    if err {
      return err
    }
    err = doMoreStuff()
    if err {
      return err
    }
  }
  err main() {
    err = someCall()
    if err is BarSpecificException  {      
      err = doErrorStuff()
      if err {
         return err
      }
    } else if err {
      return err
    }
    err = doFinalStuff()
    
    if err is BarSpecificException  {      
      err = doErrorStuff()
      if err {
         return err
      }
    } else if err {
      return err
    }
  }
If I ask you in the second piece of code what will be executed after the call to foo(), is it any easier to reply than in the first version? Personally, I don't believe so.

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

#94
post #75
post #70

Earlier quoted context omitted.

Are you familiar with Zig's error handling? Except for the fact that errors cannot contain payloads, it is, imo, perfect.

it has the same issues. You can always discard errors with `catch unreachable` for example.

If you haven't overridden the default panic handler, and you're in a Debug or ReleaseSafe build you'll still automatically get an error return trace (which is nicer than a stack trace) even if you do `catch unreachable`.

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

#95

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…

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

This is a problem with very good known solutions, yet Rust seems to fail hard.

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

#96

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

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

#97
post #36
post #2

as someone who works 95% of the time in rust, how to handle my errors is never a problem for me. I think the issue is the expectations of people coming into it with a different frame of reference, expecting the kind of traceback/exception framework built in - and for that to be a typical debugging workflow. like 95 times out of 100, I just `.map_err(|e| /* code to convert e to return type's error... */)` and I'm done…

Rust also has the traceback/exception error handling mode in the panic cases. And since there's no standard way to check those at compile time you're left to fuzz the code to find all the possibilities (or use some hacks). It would be great if the compiler could be put into a mode where panics are handled like errors that need to be explicitly handled. Maybe something like: 1) At the function level or crate level bei…

I found a crate that claims to do #1: https://github.com/dtolnay/no-panic

This also looks interesting: https://github.com/Technolution/rustig

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

#98

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

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

#99

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…

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

#100

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 think that exceptions are a problem and cause this developer burden only because they are invisible. If they appeared in the type signature, for example as () -[DatabaseReadError]-> () then they would be part of a function's 'contract'. With this, consumers of your function are making an active decision about whether to handle or bubble an exception without examining your implementation, and the type of the main fu…

This is exactly Java's checked exceptions, which I'd call a failed experiment in language design.

The problem is that it makes the most common case (letting the exception bubble up) very inconvenient and clutter-y. Signatures with 5 different declared exceptions are worse than useless.

Post reply on HN