Learn Go: Understanding Defer, Panic & Recover in Golang #13

How to properly use the defer statement for cleaning up resources

Gopher

Lets go!!

Published · Jan 21 2020


In this article, we will learn how to properly use the defer statement for cleaning up resources as well as several common mistakes that are made when using defer.


What is a defer Statement

A defer statement adds the function call following the defer keyword onto a stack. All of the calls on that stack are called when the function in which they were added returns. Because the calls are placed on a stack, they are called in last-in-first-out order.

Let’s look at how defer works by printing out some text:
package main import "fmt" func main() { defer fmt.Println("Bye") fmt.Println("Hi") }


In the main function, we have two statements. The first statement starts with the defer keyword, followed by a print statement that prints out Bye. The next line prints out Hi.

If we run the program, we will see the following output:
Output Hi Bye

Notice that Hi was printed first. This is because any statement that is preceded by the defer keyword isn’t invoked until the end of the function in which defer was used.

Let’s take another look at the program, and this time we’ll add some comments to help illustrate what is happening:
main.go
package main import "fmt" func main() { // defer statement is executed, and places // fmt.Println("Bye") on a list to be executed prior to the function returning defer fmt.Println("Bye") // The next line is executed immediately fmt.Println("Hi") // fmt.Println*("Hi") is now invoked, as we are at the end of the function scope }

The key to understanding defer is that when the defer statement is executed, the arguments to the deferred function are evaluated immediately. When a defer executes, it places the statement following it on a list to be invoked prior to the function returning.

Although this code illustrates the order in which defer would be run, it’s not a typical way it would be used when writing a Go program. It’s more likely we are using defer to clean up a resource, such as a file handle.
. . .

Multiple defer Statements

It is normal to have more than one defer statement in a function. Let’s create a program that only has defer statements in it to see what happens when we introduce multiple defers:
main.go
package main import "fmt" func main() { defer fmt.Println("one") defer fmt.Println("two") defer fmt.Println("three") }


If we run the program, we will receive the following output:
Output three two one

Notice that the order is the opposite in which we called the defer statements. This is because each deferred statement that is called is stacked on top of the previous one, and then called in reverse when the function exits scope (Last In, First Out).
. . .

Panic & Recover

Earlier we created a function that called the panic function to cause a run time error. We can handle a run-time panic with the built-in recover function.

recover stops the panic and returns the value that was passed to the call to panic. We might be tempted to use it like this:
package main

import (
"fmt"
"log"
)

func main() {
divideByZero()
fmt.Println("we survived dividing by zero!")

}

func divideByZero() {
fmt.Println(divide(1, 0))
}

func divide(a, b int) int {
if b == 0 {
panic(nil)
}
return a / b
}
But the call to recover will never happen in this case because the call to panic immediately stops execution of the function. Instead we have to pair it with defer:
package main

import (
"fmt"
"log"
)

func main() {
divideByZero()
fmt.Println("we survived dividing by zero!")

}

func divideByZero() {
defer func() {
if err := recover(); err != nil {
log.Println("panic occurred:", err)
}
}()
fmt.Println(divide(1, 0))
}

func divide(a, b int) int {
if b == 0 {
panic(nil)
}
return a / b
}


A panic generally indicates a programmer error (for example attempting to access an index of an array that's out of bounds, forgetting to initialize a map, etc.) or an exceptional condition that there's no easy way to recover from. (Hence the name “panic”)
. . .

Conclusion

In this article, we learned about the defer statement, and how it can be used to ensure that we properly clean up system resources in our program. Properly cleaning up system resources will make your program use less memory and perform better.
. . .

. . .

On a mission to build Next-Gen Community Platform for Developers