In this article, we'll learn about the usage of error handling statements defer
, panic
, and recover
in Golang with the help of examples. We use these statements to manage the control flow in the programs.
We use the defer
statement to perform various clean-up actions. For example, we use it to close the file, clear DB or HTTP connections, and so on. If there are multiple defer
calls inside a function, defer statements are run in the Last in First Out (LIFO) order after the surrounding function returns.
Let’s look at a simple function that opens the src
file and creates a dest
file. The function aims to copy the contents of src
to dest
.
package mainimport ("fmt""io""os")func copy(src, dest string) (result int64, err error) {srcContent, err := os.Open(src)if err != nil {return 0, err}dstContent, err := os.Create(dest)if err != nil {return 0, err}result, err = io.Copy(dstContent, srcContent)dstContent.Close()srcContent.Close()return result, err}func main() {fmt.Printf("copy files without defer")}
src
file and store the content.dest
file to copy the content from src.src
file to dest
file.In an idle scenario the code above works fine; but, there is a minor bug where the io.Create()
function fails, and program exits without closing the open file connection on src
.
package mainimport ("fmt""io""os")func copy(src, dest string) (int64, error) {srcContent, err := os.Open(src)if err != nil {return 0, err}defer srcContent.Close()dstContent, err := os.Create(dest)if err != nil {return 0, err}defer dstContent.Close()val, err := io.Copy(dstContent, srcContent)return val, nil}func main() {fmt.Printf("Copy Files with defer")}
src
file and store the content.dest
file to copy the content from src.src
file to dest
file.defer
to cleanup the open file connections.We use the defer
statement to close the file’s open connections if the program exits (equivalent to finally
in Ruby, and try-with-resources
in Java).
Panic
is a function that stalls the ordinary flow of control (equivalent to raise in Ruby, and throw in JS). The panic statement executes after a deferred statement. The following code example displays output three before the panic statement.
package mainimport "fmt"func main(){fmt.Println("one")defer fmt.Println("three")panic("a panic happened")fmt.Println("two")}// Output:// one// three// panic: a panic happened
print
statement before panic.defer
statement.panic
.print
statement.Here's another example of how a panic
is raised at runtime in a program.
package mainimport "fmt"func main() {x := 20y := 0fmt.Println(x/y)}// panic: runtime error: integer divide by zero// goroutine 1 [running]:// main.main()// /tmp/sandbox064477089/prog.go:6 +0x11
Recover
helps us take control when a block of goroutine panics. Recover
is only useful with deferred functions. If an application panics, it no longer executes the rest of the program except for deferred functions.
During normal execution, a call to recover will return nil and have no other effect. If the current goroutine panic's, a call to recover will capture the value given to panic and resume normal execution.
package mainimport "fmt"func main() {x := 0y := 20divide(x, y)}func actionDefer(x, y int) {if r := recover(); r != nil {fmt.Printf("Recover in divide: %v \n", r)validOps(x, y)}}func divide(x int, y int) {defer actionDefer(x, y)s, d, m := x+y, y/x, x*yfmt.Printf("sum=%v, divide=%v, multiply=%v \n", s, d, m)}func validOps(x int, y int) {s, m := x+y, y*xfmt.Printf("sum=%v, multiply=%v \n", s, m)}
actionDefer
statement has recover logic when a program panics.x == 0
, statement raises a panic
.defer
statements are run before panic, actionDefer
method is executed and recovers from the panic.In this article, we looked at the defer, recover, and panic functions and their usage in Golang.