An important rule in Go is that you should always check errors. Most people have no issues with that and are correctly checking all their errors, except when the error might come from a deferred method. This code, for example, is something fairly common in many codebases:
The issue here is that we’re not checking if the file is actually getting closed, and you might end up with a file descriptor leak. After talking with several people, the main reason they chose not to check the error is because of the added verbosity, for an error that “never happens” that is “not too bad”, or because “if it fails there’s nothing we can do anyway, and the code already did what it had to do”. While there is some truth in that, none of those reasons are valid at all. It’s better to explicitly prevent a known error from exiting the application, than ignoring it. This means that you should at the very least log the error.
Luckily for us, there are several ways to actually check the error without adding any complexity to the code. The most common way people do this is by closing the file twice:
In this example you basically get the best of both worlds: the error is being checked, and there isn’t a lot of code being added. Sadly, this doesn’t always work. It’s not rare for libraries to implement
io.Closer as a way to free any resources used, but not all of them allow you to call the method twice like
os.File does. By convention, I chose to always call my “cleanup” methods
Close , which allowed me to create a very simple yet powerful method that takes care of checking all the deferred errors without having to do add extra code anywhere (besides the helper itself):
As you can see, all you need to do is to replace
defer f.Close() by
defer close(f, &err) . That’s a mere 5 chars longer and it does everything you need. The method gets a pointer to the error and if the
Close() fails it will either assign a value to the error or log it. You can also easily improve this method to log to a different logger (or report it to datadog, sentry, etc.) as well as adding a stacktrace, some context. etc.