Defer, Panic and Recover in Go

In this blog, we are going to discuss the defer, panic and recovery statements and their role in error handling.

We use defer to delay the execution of functions that might cause an error. The panic statement terminates the program immediately and recover is used to recover the message during panic.

Defer

defer statement postpones the execution of function until after the surrounding function call returns. Defer is commonly used to make functions that perform cleanup action more simple.

Defer statements are generally paired with the open and close, connect and disconnect of resources, to ensure that resources are gracefully released before execution stops.

Multiple deferred statements are executed in reverse order in which they were deferred i.e. Last In First Out order.

Here is example of deferred function,

package main

import "fmt"

func add(num1, num2 int) {
	sum := num1 + num2
	fmt.Println("Sum: ", sum)
}

func main() {

	fmt.Println("Starting")

	defer fmt.Println("Ended")
	defer add(11, 28)
	defer add(10, 10)
}

Output:

Starting
Sum:  20
Sum:  39

Panic and Recover

The panic is a built-in function that causes the normal execution of the program to stop, but all deferred function calls are executed normally before returning to its caller. For the caller, such a function behaves like a panic and returns with panic as well. The process continues up the stack until all functions are returned from current goroutine and then the program crashes with logging error and trace.

Errors like divide by zero, accessing out of bound index of array etc can cause panics, as they are difficult to anticipate at compile time.

Following example illustrates the panic, as we try to access the out of bound index for a given slice.

package main

import "fmt"

func main() {

  numbers := []string{"One", "Two", "Three", "Four", "Five" }
  fmt.Println("number at index len(numbers) : ", numbers[len(numbers)])
}

Above program will output something like this when executed on go playground, which indicates the unexpected error occurrence and its stack trace.

panic: runtime error: index out of range [5] with length 5

goroutine 1 [running]:
main.main()
	/tmp/sandbox3999123488/prog.go:8 +0x1b

Program exited.


So Panic means execution of goroutines stops and exits, but there is a way to regain control of goroutine again by handling panics i.e recover.

Recover is a built-in function that regains the control of panicking goroutine. However, recover will be useful only inside the deferred functions, as after panic only deferred functions will be executed before stopping the execution.

Recover can be used when there is an unexpected situation, for example, panic during database connection, it may be possible to report an error to inform about the cause, before the program stops its execution.

package main

import (
	"fmt"
	"log"
)

func main() {
	defer func() {
		if err := recover(); err != nil {
			log.Println("panic occurred and handled :", err)
		}

	}()

	numbers := []string{"One", "Two", "Three", "Four", "Five"}
	fmt.Println("number at index len(numbers) : ", numbers[len(numbers)])
}

Now, above program gives output as,

panic occurred and handled : runtime error: index out of range [5] with length 5

Program exited.

In summary, the defer statement provides a powerful way for control flow which allows handling of errors efficiently, while recovery is to be used cautiously with unexpected situations caused due to panics. Defer, Panic and Recover are important to know for writing the production ready code.

Vindeep Chaudhari

Vindeep Chaudhari

Senior Software Engineer in Backend at PLG Works