Go provides ways to handle errors. Failure is everywhere—a method might work when we test it, but other conditions might cause it to panic later.
With recover, a built-in method, we get errors and can test them. We must specify a call to recover()
in a "defer" method. The Go runtime calls these methods when an error occurs.
This program introduces an error-prone method called "divideByZero." It causes an error to occur whenever it is run.
divideByZero
we specify a nested "defer" method. In this func
we assign err to the result of recover()
.nil
, we take an action that can repair the program's state and keep it from terminating.func
will never be called.package main import "fmt" func divideByZero() { // Use this deferred function to handle errors. defer func() { if err := recover(); err != nil { fmt.Println("HERE") fmt.Println(err) fmt.Println(0) } }() // Cause an error. // ... Go will run the defer func above. cat := 0 dog := 10 / cat fmt.Println(dog) } func main() { // Create a divide by zero error and handle it. divideByZero() }HERE runtime error: integer divide by zero 0
This built-in method causes an error to occur. We can handle this error in a defer method that tests the result of recover()
.
recover()
method returns panic's error. In this program, the WRONG message is provided.package main import "fmt" func explode() { // Cause a panic. panic("WRONG") } func main() { // Handle errors in defer func with recover. defer func() { if err := recover(); err != nil { // Handle our error. fmt.Println("FIX") fmt.Println("ERR", err) } }() // This causes an error. explode() }FIX ERR WRONG
Sometimes a Go developer uses the wrong variable name. For example, I wrote a program that used "string
" instead of the identifier "name."
string
is not an expression" error occurs. Use the correct identifier "name" for a working program.package main import "fmt" func mistake(name string) { // We put the wrong variable name. // ... Use "name" not string. fmt.Println(string) } func main() { mistake("cat") }# command-line-arguments C:\programs\file.go:11: type string is not an expression
The defer keyword has a performance impact. It saves the method call to be called after all returns or panics are executed in a method.
Add()
function once a return statement is reached in Test.Add()
before each return statement, and does not worry about panics.package main import ( "fmt" "time" ) type Data struct { code int } func Add(data *Data) { data.code++ } func Test(value int, data *Data) int { defer Add(data) if value == 2 { return 3 } return 4 } func TestNoDefer(value int, data *Data) int { if value == 2 { Add(data) return 3 } Add(data) return 4 } func main() { data := new(Data) t0 := time.Now() // Version 1: use defer. for i := 0; i < 200000000; i++ { Test(2, data) } t1 := time.Now() // Version 2: do not use defer. for i := 0; i < 200000000; i++ { TestNoDefer(2, data) } fmt.Println(t1.Sub(t0)) fmt.Println(time.Now().Sub(t1)) }646.339875ms defer Add 423.013125ms Add
If a method is simple, and is known to be reliable, defer is probably not needed and will just slow down the program. If it has file handling, defer is probably worth using.
With recover, defer and panic we handle errors in Go programs. This is an alternative to exception handling (like try and catch in other languages).