Yeah finally. The problem with exceptions is that you lose context about where the error occurred, and you really need to know that to handle the error properly. That's why most exception handlers just print the error.
I think the only situation where exceptions help is in constructors, but there are better solutions to that problem, like explicit object construction methods that can return an error.
I think Go's approach to error handling is the best - explicit and in context. Rust's `Result` looks quite nice though I haven' tried it yet.
Most errors shouldn't be handled; that's the reason most exception handlers simply log the error.
I've said it here before, but this is my classification of errors that exceptions are commonly used for:
1) Programming error: null pointer exception, library invariants broken, etc. These generally shouldn't be caught, and could arguably be replaced by fatal assertions, but that's typically not a polite thing to do in a library.
2) Non-deterministic failure condition: something outside the control of the calling code failed in a way that it cannot predict ahead of time. A network connection broke, a file was deleted unexpectedly, a device was detached, database server died, etc. There's something of an argument for annotating functions that can fail this way in the manner of Java's checked exceptions and it has a strong relationship with the IO monad in Haskell (and similar problems with encapsulation and hiding implementation details). These kinds of errors can sometimes be handled, and handling them with exceptions isn't much better or worse than error codes. An advantage of exceptions is that they makes it easy to abort the whole task and go back to the request handler (server code) or event dispatch loop (UI code). Exceptions can carry more state than error codes, too.
3) Business logic problem: the user, for want of a better term, of the program tried to do something that is not allowed / possible, and the program needs to abort the current task and inform the user of the problem. Exceptions are as good a means of aborting as most others. These should only be caught at the top level, whereupon they can be converted into something for human / 422 response / whatever consumption.
In almost all cases, you cannot proceed and there is nothing you can do in reaction to the error. Aborting and unwinding the call stack is usually the correct thing to do.
I happen to disagree. Exceptions are a powerful abstraction over errors that allow you to highlight the exact context of an error of handled properly by avoiding catches that just throw and actually giving the exception body useful information about the error that can be logged.
I haven't used Go (I'm about to for a small project, though) so I can't comment on the "if err != nil" idiom, but it seems like a step back by encouraging code duplication where it's not needed. I'm a fan of the pattern matching in rust, but it's still rather orthogonal to exceptions as it allows you to deal with an error when you try to access state whereas exceptions are most useful when they appear mid operation.
I don't agree neither :). Exceptions don't necessarily lost context. At least in Python. In my previous job, whenever a python exception raise, the code will catch it, with values of each variable, formatted in a beautiful html, and send as an email. You can never overestimate how much time it has saved us to debug. Go's approach to error handling remind me of the old COM days, while you handle error after each function call. I'm glad it's no longer popular.
Agree! I tried to move a program from Python to Go. Completely gave up on Go after comparing two language's implementation on exception. Python is just so much easier, cleaner.
I think the only situation where exceptions help is in constructors, but there are better solutions to that problem, like explicit object construction methods that can return an error.
I think Go's approach to error handling is the best - explicit and in context. Rust's `Result` looks quite nice though I haven' tried it yet.