Go defer, panic, and recover
We use defer
to delay the execution of instructions, panic
to halt the execution of the program immediately and recover
built-in function is used to recover from a panic.
In this chapter, we will look at new ways to handle errors in Go, through the use of the defer
keyword and of the panic
and recover
functions.
defer
The defer
keyword is used to delay the execution of an instruction until the end of the function.
In the following example, we have three print statements.
- The
Println
that hasdefer
executes at last before the function end. - The other two
Println
executes before in thedefer
statement.
package main
import "fmt"
func main() {
// defer the execution of Println()
defer fmt.Println("I am number 3")
fmt.Println("I am number 1")
fmt.Println("I am number 2")
}
Output
I am number 1
I am number 2
I am number 3
The most common use of defer
is to close a file:
func main() {
file, err := os.Open("file.txt")
if err != nil {
// handle the error
}
defer file.Close()
// body of the function
}
This way, it is easy to check that the file is going to be closed when Compared with the following traditional code:
func main() {
file, err := os.Open("file.txt")
if err != nil {
// handle the error
}
// body of the function
file.Close()
}
If the main function returns before the file.Close()
instruction is executed, or if an error occurs, the file will not be closed.
Multiple defer statements in Go
We can have multiple defer
statements in a program. The execution order, in this case, would be LIFO (Last in, First Out). It means the last defer statement will get executed first.
In the following example, we have added defer
to all three Println
statements. Notice the output the last statement gets executed at first, and then the first statement gets executed at last.
package main
import "fmt"
func main() {
defer fmt.Println("I am number 1")
defer fmt.Println("I am number 2")
defer fmt.Println("I am number 3")
}
Output
I am number 3
I am number 2
I am number 1
panic
The panic
function is used to stop the execution of a function and to return it to the caller. The statements after the panic
function will not be executed.
It is similar to errors, but it is used as a last resort.
package main
import "fmt"
func Divide(a, b int) int {
if b == 0 {
panic("ERROR: cannot divide by zero")
}
return a / b
}
func main() {
fmt.Println(Divide(4, 2))
fmt.Println(Divide(4, 0))
fmt.Println("Execution Completed")
}
Output
2
panic: ERROR: cannot divide by zero
The panic keyword in output indicates that the program is terminated due to panic, and it prints the relevant panic message along with it.
More Real World Example:
In the following example, if the file open fails for any reason, it creates panic and stops the execution of the code.
func OpenFile(name string) *os.File {
file, err := os.Open(name)
if err != nil {
panic(err)
}
return file
}
recover
Finally, the recover
built-in function is used to recover from a panic.
Here is a trivial example to illustrate how it works:
func main() {
var x := 0
defer func() {
if err := recover(); err != nil {
fmt.Println(err)
}
}()
if x == 0 {
panic("ERROR: x cannot be zero")
}
}
In the above snippet, the recover
function is called just before the function returns, and the error is printed rather than the program crashing.
But using panic
and recover
is not recommended if you can avoid them and implement the error
interface.