I love defer, it helps you avoid problems by forgetting to unlock your mutex, or close your file, or whatever at a given return point. But there's one thing I don't like about it: it encourages you to ignore errors.
I like Go's error handling, I don't have a problem with the fact that it's "easy" to ignore errors, because you at least have to explicitly ignore them with _. So if it blows up, it's really your own fault. The problem with defer, though, is that in order to check an error you have to go out of your way.
Take the common example:
func DoStuff() (string, error) {
myFile, err := os.Read("filename")
defer myFile.Close()
if err != nil {
return "", errors.New("Couldn't open file: " + err.Error())
}
// Do stuff
return someString, nil
}
Do you see anything wrong with this code? Well, you use defer so often you may not remember, but Close returns an error! And you're ignoring it! It hasn't been fed in a month and you don't even acknowledge its existence!
Now, if you do want to check the error, you can. I'm not arguing that you can't. The problem is, it's weird. You might think to write a handler function:
func ErrorHandler(errFunc func() error) {
err := errFunc() //... uh... okay, now what?
}
Okay, so maybe that doesn't work
func ErrorHandler(errFunc func() error, err *error) {
*err = errFunc()
}
Might be okay, now you can call
defer ErrorHandler(myFile.Close, &errorToReturn)
At least, I assume the address is still valid in a deferred function called after return.
Obviously you can use an anonymous function closure and assign the error to the named return value too, but then you have to rewrite, or copy/paste your error handling closure every time you defer an error throwing function. The other problem is that error handlers aren't necessarily portable, if the function takes different arguments you have to write a new handler for it.
I don't think this is good, it's one thing to allow a user to blatantly ignore an error with little effort. At least they're explicitly ignoring it, and it's really their own fault. It's quite another to have a commonly used language idiom that encourages programmers to ignore errors, and makes them expend extra effort just to make sure their program didn't do something weird. Fortunately, an error closing a File is pretty uncommon and usually won't cause anything too bad to happen, but checking errors is all about handling "shouldn't" and not just praying that what's usually okay still is. Not to mention that it's perfectly conceivable to have a deferrable function where the error it might return is not mostly superfluous. Unfortunately, I don't really have any ideas to fix it. I think disallowing defer to be used on functions with return values is going too far, and compiler warnings are out of the question. I still feel like there needs to be some mechanism to not force programmers to expend extra effort to check an error, because that just leads to Bad Things™ happening. In a perfect world, programmers are going to check every error. In the real world a lot of programmers are going to go.
Thoughts?