1) "if err != nil" after every statement
2) give some serious thought to whether the previous statement could panic or not
Good luck if it's a library call that may get updated or call other libraries !
3) Think about the non-error error cases that can't be abstracted out. Go is like C, in the sense that there is an ERETRY "error" (unsurprisingly, you should simply try again, you should NOT fail)
And there are cases where there can be an error and yet error is nil. Easy example of this would be sscanf.
And we now see practical Go code published online : how these problems are dealt with, real world edition:
1) either mindlessly putting "if err != nil { return err }", which is a very bad information-erasing exception system, or just outright ignoring errors. Don't you know you can also use "_" as the error variable ? Maybe they should make that implicit like in perl. Of course perl is likely to tell you this happened ... unlike C and Go.
(really brings back the C days doesn't it ?)
2) most people either don't know or just deny this. Thankfully panics at least do list where they occur. They also kill your program and print stacktraces. Pages and pages and pages of stacktraces.
3) very few people even know about these problems ... so they're ignored, and the standard Go tools themselves don't behave according to unix specifications.
You should really use a library like github.com/pkg/errors so you get to wrap the error you return with additional information. Errors are just a worse Either monad after allm they're much more pleasant to use than exceptions.
How are errors in Go at all monadic? They just have a convention where you return a tuple and manually check if something isn't nil. Either type will inhabit one variant or another, not both with one having a value of nil.
The 'monadic' part of Either (or Result in something like Rust, where try! is sort of like >>= if you squint) is the ability to chain Either types together and have the boilerplate abstracted, that feature is completely absent from Go. Errors in Go are neither the Either type nor monad, IMO.
That's it. I think OP just assumed panics in Go are what exceptions are in other languages.
And there’s nothing like checked Panics so you’d know if one will happen or not.
Calling a number generator with a negative limit is also a programmer fault, so no reason to provide an error here.
X includes
the language authors (who are using this more and more in the stdlib as pointed out elsewhere)
the authors of any library you use ... but
transitively, so this includes the authors of any library you use indirectly as well
Hmmmm ... what was the problem with (unchecked, or python's) exceptions again ?
And those panics do give you a helpful stack trace, complete with source code line numbers, so it's easy to find the culprit (as opposed to "bubbling up" exceptions).
The canonical use of checked exceptions in Java is for unpredictable events - almost always related to interaction with the outside world, like IO, networking, parsing, etc. These are things the programmer can't prevent, and must defend against, so the type system allows, and in fact forces, the programmer to explicitly address them.
This is all explained beautifully, and at length, in this monograph:
Well, imagine a game letting the user roll a dice. "Chose a number of sides for your dice".
In such cases, handling such a panic might be useful. And knowing that it exists might be useful, too.
If the language does not allow specifying a range of a type (for example, requiring all numbers passed into the RNG to be positive), then it should be specified in the API in other ways programmatically, so it can be statically analyzed.
In languages with dependent types, for example, it’s common to represent a Stack in a way that number and type of elements are stored in the type (so you can’t even pull from an empty stack – that’d be a compile time error).
In the same way, the random number generator should either return an error, or use a number type that can only encode positive numbers as input.
Especially if combined with the interface{} everywhere across the new stdlib functions this all smells very much like C's problems.
- Should I try to catch the exception, or just let it
bubble up and edit my interface to include it?
- Should I create a new exception type or reuse an existing one?
- Should I throw a checked or unchecked exception?
- Am I exposing implementation details via my interface?
(eg I don't want to throw an SQLException from GenericDataSourceWidget.connect())
In golang, I know there's really just the one pattern: check if err != nil, prepend a descriptive message, and return it.That'd be the RuntimeException equivalent, sure. But what do you do for the equivalent of checked exceptions? Errors are frequently recoverable, "err != nil" alone does nothing to help you there, and string manipulation is a horrific alternative to types.
- Should I try to catch the exception, or just let it
bubble up and edit my interface to include it?
- Should I create a new exception type or reuse an existing one?
- Am I exposing implementation details via my interface?
(eg I don't want to throw an SQLException from GenericDataSourceWidget.connect())
(except for "- Should I throw a checked or unchecked exception?" since that's fairly Java-specific)To me, those questions seem unavoidable, and sweeping them under the rug is a false simplicity. You're exposing things - what do you expose? How should the caller deal with it? Is it the same as [other thing]? I'd much rather have the type system involved, since error handling is pretty critical to correctness/stability. If go's giving up the safety, what does it get in return?
This is like saying "I'm happy to avoid the cognitive load of having to specify how my code should behave in case of an error".
Sure, your code is simpler as a result. It's also more buggy.