Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Good article. I've always argued that blind ?-style shortcircuiting in Rust is extreme code smell and guaranteed pain for future production outages, and that it's not an actual solution to error the handling problem (which seems to come up often in Rust/Go flamewars). And I'm glad that this idea is picking up traction.

Error handling is hard, Go doesn't hide the complexity, but a lot of other languages try to handwave the complexity away. Either way, programmers are by default lazy, and you end up with "Error 500: connection refused".



Granted, even though Go shoves its errors in your face, it's still common to just punt the error up the stack (if err != nil { return err }), leading to ahem "context-free" error messages like "Failed to initialize Foo: EOF".

You can improve on this a little by using something like https://github.com/pkg/errors, which gives your errors a stack trace by default. But now your errors are hideous and unlikely to be fixable by the average user.

Ultimately the only way to get clean, informative, and debuggable error messages is for the programmer to attend to each and every one. And no one wants to do that, because it's a pain, it slows you down, it gets in the way of solving The Problem At Hand. So unless the language itself somehow forces you to wrap all your errors in nice context strings, it ain't gonna happen.


> Error handling is hard, Go doesn't hide the complexity, but a lot of other languages try to handwave the complexity away. Either way, programmers are by default lazy, and you end up with "Error 500: connection refused".

Except that (no pun) Go has both exceptions in the forms of panics AND its horrible error interface. I don't like exceptions but I'll take exceptions over Go glorified return codes anytime. No amount of "decorating" that system makes it better. There is nothing to it. Rust is a bit better on that matter BECAUSE Rust has the syntactic sugar to make errors as value painless to deal with. Go doesn't.

Sometimes on your server, you have 15 IO operations to perform, you just want to log on failure and return error 500 to the user. This is the case where go doesn't shine at all, what do you do? aside from being tempted to resort to a GOTO statement?


You check each one for an error and return a 500 if there's an error. It takes a few more seconds to write but is incredibly easy to read and maintain.

I'd argue that this article is evidence that Rust's syntactic sugar doesn't really help, it just seems like "official" encouragement to do the wrong thing (pass the buck).


> It takes a few more seconds to write but is incredibly easy to read and maintain.

I disagree.

    try{
        dothis();
        /* ... 14 lines of dothis(); like */
    catch(error){
        log(error);
        return responseCode(500);
    }
sounds easier to both write and maintain to me. Only a handfull of times you'd ever want to deal with each exception separately. And when you do, it's not much more verbose than if err!=nil{ ... }.


.. except in Java and Python, where you also get 100-line stack trace in your error log. Or C/C++, where you get a full coredump with program state as long as you set up your system correctly.

Let's not forget there are more options than Go's and Rust's approaches. If some languages made error handling hard, it does not mean it is hard in general.


One of my longest and most frustrating debugging sessions was with a Java project where some developer was fed up with 100-line stack traces and wrote the following:

    catch (Exception e) {
        log.error(e);
    }

Which caused the following error output

    ERROR: could not connect
(mentally adjust to Java syntax, error message, don't remember off the top off my head)

I'm not joking. Having overly-verbose errors by default will make people just swallow everything at some point or another.

That particular example was triggered, IIRC, by a configuration file containing a malformed IP address, which was interpreted as a hostname, which resolved into an NXDOMAIN, which caused a magic connect-socket-to-dns-name-or-ip-address call to fail with a generic message when that socket was lazy-connected in an unrelated code path.


Yep, people keep doing this. I think it depends on background.

If the app is to be used by user unfamiliar with language, and there are no robust error reporting systems -- think website or desktop app -- then this approach actually makes sense. But in the environment where most investigation is done via the logs, and many bugs are not trivially reproducible? Not so much.


Following through to the logical conclusion, you would want some way to switch between simple logs and context-rich logs. We've reinvented the verbose flag!


btw. there are error loggers which would print a stack trace in that case. i.e. slf4j. you can even do log.error(e, "blablabla");




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: