The Go defer
statement allows you to schedule a function to be called immediately before the surrounding function returns.
If you are familiar with Python, you probably know about Python’s with
statement. The goal of this construct is never to forget freeing or closing resources. The Go defer
statement was added to Go for precisely the same reason. In this article, I’ll explain what defer
is, why it’s useful, and when and how to use it.
What is Go’s defer statement?
The defer
statement in Go allows you to schedule a function to be called immediately before the surrounding function returns. It is often used to perform cleanup tasks, such as closing a file, a database connection, or unlocking a mutex.
The defer statement:
- helps us keep related code close together, e.g., the opening and closing of a file.
- Helps in not forgetting to free up resources (like open files)
Let’s look at an example:
file, err := os.Open("myfile.txt")
if err != nil {
// handle the error
}
defer file.Close()
// read and process the file
Code language: Go (go)
When we open a file and that action was successful (no error), we directly tell Go to close the file as soon as this function ends using a deferred close call. The defer statement doesn’t add much value as long as our code follows the happy flow (everything is going as planned). We could just as well put the file closing operation at the end of the function, a pretty logical spot to close resources.
But what if everything doesn’t go as planned? What if an error occurs right in the middle of our function? We want to return an error, but we also have to think about closing the file. Chances are we will forget, creating a resource leak. This is why defer
makes life a lot easier for Go programmers. We already told Go to close the file whenever we exit the function, even when we exit with an error!
Last in, first out
When a function returns, its deferred calls are executed in last-in-first-out order, often abbreviated as LIFO. The following example demonstrates this:
func foo() {
defer fmt.Println("defer 1")
defer fmt.Println("defer 2")
defer fmt.Println("defer 3")
fmt.Println("foo")
}
foo()
// Output:
// foo
// defer 3
// defer 2
// defer 1
Code language: Go (go)
Good to know about Go Defer statements
Some important things to keep in mind:
defer
statements can take a function call as an argument. The arguments to the function are evaluated when thedefer
statement is encountered, but the function itself is not called until the surrounding function returns.defer
statements are executed even if areturn
orpanic
statement is encountered in the function. This makes it helpful in performing cleanup tasks even in the event of an error or abnormal termination.