How to use error control flows in Golang
Error control flows in Golang
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.
Defer statement
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.
Example
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.
Code
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")}
Explanation
- Line 10: Open the
srcfile and store the content. - Line 15: Create
destfile to copy the content from src. - Line 20: Copy the contents of
srcfile todestfile. - Line 21 & 22: Close the open file connections.
- Line 12 & 17: Return with an error when file handling functions error out.
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.
Code
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")}
Explanation
- Line 8: Open the
srcfile and store the content. - Line 16: Create
destfile to copy the content from src. - Line 21: Copy the contents of
srcfile todestfile. - Line 14 and 20: Use
deferto cleanup the open file connections. - Line 12 and 18: Return with an error when file handling functions error out.
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 statement
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
Explanation
- Line 4: Use the
printstatement before panic. - Line 5: Use the
deferstatement. - Line 6: Raise a
panic. - Line 7: Unreachable
printstatement. - Line 10 and 13: Output of the program.
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 statement
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)}
Explanation
- Line 11: The
actionDeferstatement has recover logic when a program panics. - Line 21: if
x == 0, statement raises apanic. - Line 19: As
deferstatements are run before panic,actionDefermethod is executed and recovers from the panic.
Conclusion
In this article, we looked at the defer, recover, and panic functions and their usage in Golang.