> tmp/sandbox459432416/main.go:10: err declared and not used
Go won't compile unless you handle your errors.
https://play.golang.org/p/UBL0MZtscc
> tmp/sandbox307381845/main.go:8: multiple-value "html/template".New("foo").Parse() in single-value context
You would have to explicitly assign the err return value to underscore in order to ignore it.
Assigning errors to underscore is effectively the same as try { myFunc(); } except { pass; }, making them equivalent.
Stack unwinding (with 'finally' clean-up blocks) results in higher brevity and relatively cleaner code than having a err != null check after every function call. Not to say, checking err != null is optional, could be accidentally forgotten, and cause unpredictable runtime behavior.
Case 1: checked exceptions. This is what Java used heavily in the early days, a bit less today, which basically requires you to deal with known exception scenarios. It was good because it wouldn't compile unless you explicitly said "I'm not dealing with this" or you dealt with it. But its bad because it requires your methods to throw some list of exceptions all the way up the call stack. I think there are some other problems that Experts(tm) around here can probably point out better than I. Still, checked exceptions seem to be a valid pattern in some contexts, and this feels like the same pattern Go follows. That said, I dislike Go's error handling for the same reason: my code is really cluttered and unclear this way.
Case 2: when you catch everything, you may not throw the right thing to your caller(s). Even worse, you may swallow an exception unintentionally and leave callers hanging in the balance. It's not that it's impossible or particularly difficult to avoid making these mistakes by wrapping everything in a Try[T], just that it's easy to make the mistake of catching more than you intended.
The code for both functions make it pretty clear that an error will only be returned if the input is invalid, which is not something that will occur with the known constants being fed into them.
Looking at the implementation is no guarantee that the implementation won't be changed in future.
That isn't quite the same thing. That says that an error is returned only because it is required to conform to the io.Writer interface. Without that requirement, it wouldn't return error in the first place. It tells the reader that they should not be confused as to why writing to a buffer might return an error, when such an operation should fundamentally not return an error.
These other functions in question have very good reasons to return an error: Invalid input.
> Looking at the implementation is no guarantee that the implementation won't be changed in future.
The Go compatibility promise says that the behaviour won't change, except under exceptional circumstances – like a security flaw that cannot be fixed using the original behaviour. It is highly unlikely that is an issue for those particular functions.
But, if you still think it is important, what do you plan to do with an error that might just randomly appear in the future anyway?
Terminate processing and pass the error back up the call chain. This is basically what exceptions do, and basically what 'if err != nil { return err }' does.
One could also pause processing, interrogate the call chain for what to do about the error condition and the proceed as directed; this is what conditions & restarts do. IMHO it's a better solution, but it requires some syntax to be readable.
The function in question that could return an error is being called in the main function. Who are you going to pass it to, exactly? error is a not a return value on main. If you knew what the error could be, you might find a suitable workaround under those conditions. But since the error does not even exist for you to handle, about all you can do is terminate the application. Which is exactly what will happen in this circumstance anyway, even if you don't check the error result.
I'm still not clear what you are really going to meaningfully do with the error information here even if you did magically start getting errors against the compatibility promise? If you are going to handle errors, you actually have to know what you want to do with them. The following adds nothing to your code.
if err != nil {
// I didn't expect that. Oh well.
}
It's not like anyone is suggesting you should avoid checking errors in all circumstances. We're talking about very specific cases where all of the conditions are known. If GET stops being an HTTP request method, or http://example.com/ is no longer a valid URL, you've got way bigger problems than not gracefully handling the world flipping upside down.The operating system: exit with a non-zero exit code, indicating the specific error.
Anyways, the point is I don't understand the point of err != nil bashing here, given there are two programs in the article that only contain one error check each.
That said I can't think of an alternative other then to reduce the number of panic()s hidden in various libraries.
All other error conditions are expected and handled. That's what Go's error handling philosophy is all about.
What sorts of things fall into this category, and what tradeoffs you're willing to make to have the computer do things for you, is a matter of application (and taste), and so we reasonably have many different programming languages. But it's entirely defensible to want, in the abstract, your language to do repetitive and important work for you. That's what computers are good at.
Errors are valid values, handling them is as normal as handling other values you get from functions.
let foo = try!(something_that_might_error())
// or soon...
let foo = something_that_might_error()?
The try!() macro or the ? operator unwrap a Result<T, E> value, which is basically "either the return value or an error value". If it's a return value (T), the macro/operator just gets the value out of it - but if it's an error (E), it will convert the error into your own function's Result<T, E> return type using the From trait, and return that from your own function immediately.The practical result? When I'm parsing input or doing other things that are heavy on errors, I can split it out into another function, wrap every call that might fail in try!(), and only have to actually handle the error once. This allows me to see my code clearer - I can see the success case very clearly, but I'm still forced to decide whether to handle or return errors by the compiler, because there's no other way to get the T out of a Result<T, E>.
There's a few other languages which implement this, though they lean towards the functional side a lot more than Rust does.
Experience: I'm writing a small internal certificate authority in Go because it has x.509 and signing support in the standard library. I'm no safer by having roughly 3/5 of my lines be if err != nil { return err; }.
Go already has plenty of complicated language primitives with complex behaviour.
But truth is it is the programmers who are lazy, we should not blame language for providing a feature which some programmer abuse.
I have never seen a Java programmer profiling their code for Memory or CPU at work and I am here running my shitty C code under Valgrind and a profiler all the time.
I guess, it is programmers and not the language.
Go's error != nil approach though rudimentary actually forces lazy programmers to complain, since they cannot abuse it.