Defer Functions in Golang: Common Mistakes and Best Practices
This story will focus on defer functions in Golang, providing a comprehensive guide to help us understand them better.
In Golang, defer functions give us the ability to execute a statement just after the surrounding function ends. It’s a logical delay that can help us run a task after a function exits, regardless of an error occurs or not.
Let’s see en example:
We should know defer statements will run in LIFO (Last In, First Out) order:
And when there is a panic
:
It is very important to understand defer functions will run after panic
at the end of surrounding function. So, when we want to recover from a panic situation, we should use a deferred function as shown below to catch the panic error: (Again, note LIFO order)
No “panic happened” is printed because panic recovered by recover()
. The call to recover()
works only if it's called directly in deferred function and it should not be used to call another func
.
Let’s Go further
Now that we have a fundamental knwoledge of the defer functions and statements, let’s dive into some practical use cases.
1. Common Mistake: Immediate defer Close()
Imagine a scenario where we’re dealing with a file. After doing some certain operations, we might write a defer call as below:
This approach is quite wrong and if the file creation fails the file
variable might be nil
, which will lead the program to a runtime panic. We want to improve this, but the following attempt is still incorrect:
I’m not gonna lie to you, this code will usually run without any error but if something bad happens, it will cost you a headache to understand what is wrong!
Because when we use the defer
keyword before a function, we are ignoring any potential error
it might return. This makes our code less predictable and harder to debug over time!
As the saying goes, third time’s the charm, and let’s get it right: (Obviously, you can extract the anonymous func
into another function)
Not only this scenario, but any other action like calling an API and closing HTTP response body and etc can make your application to start to behave abnormal!
2. For Loop and Defers
Another thing to be cautious about is writing defer functions inside for
loops. Look at this example:
We expected the first printed value to be 0
, but using defer
keyword will delay the result, stack them and by LIFO behavior the result will be 43210
.
If we have a file closing defer statement inside a for loop, we must know that closing the desired resource won’t happen until execution of surrounding function ends. For example:
The code above can cause a resource leaks or random results. There are multiple ways to fix current issue and one of them could be using an anonymous function like this:
So, that’s it. Here is the summary of what we have reviewed so far.
Summary
- We can use
defer
forrecover
from apanic
- Calling to
recover()
function works only if it's called directly in deferred function - We can delay some statement to run at the end of surrounding function
- Defer functions will not return
error
to handle them directly - We should Be careful about
for
loops anddefer functions
to avoid resource leaks
If you like this article please Clap 👏 and follow me to get more of these Golang stories.
ALSO READ: