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.