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

Every Go article explains this, but it doesn't answer the higher level question of why not use exceptions. Even worse you are forced to return data values along with the error code/flag/object.

I've not seen a standard error object either which means every place that looks at an error has to be intimately aware of what it is looking at. There is an os.Error but that appears to be string only so there is no errno equivalent value inside.

In languages with exceptions there is a base error/exception/throwable that has reason fields as well as methods to get tracebacks which are extremely useful.

I'm now 85% convinced Go made a mistake not using exceptions.



If you're going to do error handling right in an exception-using language, you really need to catch every exception close to its source, otherwise you won't know how to handle it properly.

I think Go's main approach is this: errors are not exceptional. Errors happen all the time, and should be considered part of the normal control flow of the program, not something to be shoved into a hidden compartment and dealt with later.

Thus using the usual control flow statements to check for errors is entirely appropriate, and it's immediately obvious when reviewing the code if someone has failed to do the right error checking.

BTW there is no os.Error type any more. There's a language-defined "error" type, which is an interface containing a single method:

type error { Error() string }

You only need to be intimately aware of what you're looking at if you want to take some different action based on the kind of error. This is actually quite rare (usually you care only that something failed, not how it failed), but when you need to, generally the calling package defines an error type that represents a particular kind or class of errors (for example http://golang.org/pkg/os/#LinkError)


When you have exceptions you can decide how many levels of stack frames higher to handle it, and none of the intermediary functions have to be modified. With this mechanism, every intermediary must handle the error and must agree on the error type (to some degree).

The argument that keeps being trotted out for Go's approach is consistency of handling, which is a good thing. But it is very manual especially where there is distance between the code that finds an error and the code that decides what to do about it. Doing all that manual work doesn't seem to have any benefits to me.


An error isn't just about what went wrong - it's also about what you were trying to do when it went wrong, so you can do something appropriate. If you handle exceptions several stack frames up, then you lose that information. Doing things the Go way also means you can make nice error messages that reflect the task, rather than a stack trace that only makes sense if you know the source code.

Yes, every intermediary must agree on the error type - it's the language-defined error type, which defines a single method, Error, which returns the error as a string. There's no need for any further agreement.

Functions encapsulate errors. If I call a function, it is entirely up to me how I wish to handle that error. "The code that finds an error" is the code that calls the function. All the context is local. There's no need to know how that function first encountered that error - that's part of the implementation detail of that function. At every stage, we make error handling decisions based on local context. This makes the code more maintainable, because there is genuine separation of concerns.

If you really want exceptions (a classic example is a recursive descent parser where you don't want to check every call), you can use panic and recover, making sure that callers will never see it - it's a local contract only.


How do you lose information with exceptions? You can handle an exception in the code immediately surrounding whatever detects the problem and your code semantics are no different than Go. The Go mechanism doesn't give the option of handling it several stack frames higher without having to implement handling in every single intermediary function.

The arguments I keep seeing for Go's semantics seem to the same as the ones about manual memory allocation - you must be in control every step of the way.

It looks like panic only takes a string so it isn't a good equivalent to exceptions. Whatever gets flung around should generally have enough information to make decisions and to generate meaningful error messages.




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

Search: