In Go, the defer statement is used to ensure that a function call is performed later in a program’s execution, usually for purposes of cleanup or resource management. In this blog, we’ll explore how defer works in Go and how it can be used effectively in your code.
The Basics of defer
The defer statement in Go schedules a function call to be run immediately before the function executing the defer statement returns. Here's a simple example:
func main() {
defer fmt.Println("world")
fmt.Println("hello")
}
When we run this code, it outputs:
hello
world
Notice that the call to fmt.Println("world") happened after the call to fmt.Println("hello"), even though it appears first in the code. That’s because the defer statement schedules the call to fmt.Println("world") to happen after the current function (main) has finished executing.
defer statements are executed in the reverse order that they are declared. This can be important if you have multiple defer statements in a function. Here's an example:
func main() {
defer fmt.Println("three")
defer fmt.Println("two")
defer fmt.Println("one")
fmt.Println("hello")
}
This code outputs:
hello
one
two
three
The defer statements are executed in the reverse order that they are declared, so fmt.Println("one") is executed first, followed by fmt.Println("two"), and so on.
Why Use defer?
So why would you use defer? There are a few reasons:
1. Clean Up Resources
One of the most common uses of defer is to clean up resources that a function has acquired. For example, if you open a file in a function, you can use defer to ensure that the file is closed when the function returns:
func readFile(filename string) ([]byte, error) {
f, err := os.Open(filename)
if err != nil {
return nil, err
}
defer f.Close()
return ioutil.ReadAll(f)
}
The defer f.Close() statement ensures that the file is closed when the readFile function returns, even if an error occurs.
2. Debugging
defer can also be useful for debugging. If you're not sure where a function is returning, you can add a defer statement to log its return value:
func someFunction() error {
defer func() {
if r := recover(); r != nil {
log.Printf("recovered from panic: %v", r)
}
}()
// ...
}
In this example, we’re using a defer statement to recover from any panics that occur in the function and log the error message.
3. Function Timing
defer can also be used to time how long a function takes to execute:
func someFunction() {
defer timeTrack(time.Now(), "someFunction")
// ...
}
func timeTrack(start time.Time, name string) {
elapsed := time.Since(start)
log.Printf("%s took %s", name, elapsed)
}
In this example, we're using a defer statement to time how long someFunction takes to execute and log the result.