Live data from Hacker News

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

msirringhaus.github.io

101–110 of 204 posts

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

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

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

#102

Earlier quoted context omitted.

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…

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(
        SomeProcessor processor,
        Container things,
        Parameter how) throws E {
        // ... {
            processor.process(extractedThing);
        // } ...
    }
and then later:

    void processOrFail(Thing thing)
        throws SomeCheckedException {
        // ...
    }

    void processOurThings() {
        try {
            someObject.processThingsSomehow(
                this::processOrFail,
                getThings(), HOW);
        } catch (SomeCheckedException e) {
            // ...
        }
    }
and it definitely worked the way I expected—if the correct exceptions weren't caught in processOurThings, it wouldn't compile, and processThingsSomehow did not have to catch them. It even worked in at least some cases with multiple throws on the concrete SomeProcessor, though I think the different exceptions involved had an upper type bound within the package; I don't know how well that's handled in the general case.

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

#103

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…

Well, what you're saying is exactly the reason why java called them Exceptions and not Errors (well, Error also exists, but it is generally reserved for very nasty problems). Anyway, you can't just define the problem away, or else you end up with Go style error handling - that is exactly what a language with no built-in support for errors looks like. Languages need to offer control flow mechanisms that allow you to s…

Rust actually used to use conditions for its I/O errors, but they were removed as part of the “new runtime” project in 2013, for reasons that are dimmed in my memory by time, but I think they included: quite a lot of complexity, including in ways that had negative performance implications; lack of clarity about where errors could occur; lack of ability to distinguish between errors by source in the handler; more limited potential than hoped in ways that were probably connected to Rust’s ownership orientation; unfamiliarity to users (and Rust had already spent its weirdness budget); and that the actual power of conditions was almost entirely unused (I honestly don’t think I saw any production-like code use handlers for anything interesting, ever).

You can still readily express the concept of conditions in Rust, but it’s no longer baked into the standard library.

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

#104
post #36

Earlier quoted context omitted.

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

Yep, the no-panic crate is the hack I mentioned. It's using the linking process to fail compilation if I remember correctly. It only works on individual functions. rustig I didn't know and looks very interesting, thanks. Having it integrated in the compiler as annotations and guarantees would be ideal.

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

#105
post #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 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:

    thread 'main' panicked at 'dumping failed', src/main.rs:2:5
    note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
The OP has omitted the next line, which tells you how to receive a backtrace. Backtraces are off by default in Rust because, unlike Java, Rust has a lightweight, C-style runtime that tries not to impose any runtime cost that the programmer did not ask for.

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

#106
post #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 t…

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

As a rust developer, there are other ways to skin a cat, or an Error return as it were. For the std::net library (I've been using it when dealing with tcp/udp sockets), when a socket returns an Error, it uses std::io::Error [1]. This is basically a struct that implements the display and Error traits, it stores an enum inside of the types of errors that you may want / need to ignore/ handle. You don't technically need to use libs to do anything error related.

This code has been round since Rust 1.0. Imho, this is kinda how Rust should tell people to make/handle errors. I like it. But I also have found rust enums to be extremely powerful in a lot of use cases.

[1] https://doc.rust-lang.org/std/io/struct.Error.html

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

#107
post #76

Earlier quoted context omitted.

But design and coding are different steps, best kept separate. When you are designing, I agree with you, the expected usage is very important. But in the design step the particular language mechanism for dealing with conditions does not matter. Once you get a specification to program to, all input conditions can be treated as equal. That is, unless you need to optimize heavily by biasing your execution path for a cer…

> Once you get a specification to program to, all input conditions can be treated as equal. And if the spec says "try downloading the file 3 times at 5 seconds interval; if that fails, give up the update", I am free to implement it however I want (?) unless I missing your point.

sure! for example, in your case you only need a for loop and an if/else statement

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

#108

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…

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…

computers don't do "weird" things. If you think this is the case it means that the interface you are using is incomplete or not well-specified.

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

#109
post #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 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 this point. And that many of the things we take for granted in the language today did not always exist. I still remember when generics were released. Get off my lawn.

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

#110
post #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 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.
Post reply on HN