Recover in Go


Recover in Go

In this tutorial, we are going to discuss about Recover in Go language. Go language provides a built-in function recover for recovering from a panic. Below is the signature of this function

func recover() interface{}

We already discussed that defer function is the only function that is called after the panic. So it makes sense to put the recover function in the defer function only. If the recover function is not within theย defer function then it will not stop panic.

package main

import "fmt"

func main() {
    names := []string{"Ashok", "Sai", "Rama", "Seetha"}
    checkAndPrint(names, 5)
    fmt.Println("Exiting normally")
}

func checkAndPrint(names []string, index int) {
    defer handleOutOfBounds()
    if index > (len(names) - 1) {
        panic("Out of bound access for slice..!! Please verify index before access..!!")
    }
    fmt.Println(names[index])
}

func handleOutOfBounds() {
    if r := recover(); r != nil {
        fmt.Println("Recovering from panic:", r)
    }
}

Output

Recovering from panic: Out of bound access for slice..!! Please verify index before access..!!
Exiting normally

Run in playground

Explanation

In the above example we have a function checkAndPrint which checks and prints slice element at an index passed in the argument. If the index passed is greater than the length of the arrayย then the program panics.ย 

Now we have added a defer function named handleOutIfBounds as well at the start of the function checkAndPrint.ย  ย This functionย  containsย  theย  call to recover functionย as below

if r := recover(); r != nil {
    fmt.Println("Recovering from panic:", r)
}

So the recover function will catch the panic and we can also print the message from the panic.ย 

Recovering from panic: Out of bound access for slice..!! Please verify index before access..!!

After the recover function the program continues and the control returns to the called function which isย  main here. That is why we get output as

Exiting normally

Theย  recover functionย  returns the value which was passedย to the panic function. Therefore it is a good practice to check the return value of the recover function.

If the return value is non nil then panic did not happen and recover function was not called with the panic. That is why we have below code in theย  defer function handleOutofBounds

if r := recover(); r != nil 

Here if r is nil then panic did not happened. So if there is no panic then call to recover will return nil.

Please note that ifย the defer function and recover function is not called from the panicking function then in that case also panic can be recovered in the called function as well.

package main

import "fmt"

func main() {
    names := []string{"Ashok", "Sai", "Rama", "Seetha"}
    checkAndPrintWithRecover(names, 5)
    fmt.Println("Exiting normally")
}

func checkAndPrintWithRecover(names []string, index int) {
    defer handleOutOfBounds()
    checkAndPrint(names, 5)
}

func checkAndPrint(names []string, index int) {
    if index > (len(names) - 1) {
        panic("Out of bound access for slice..!! Please verify index before access..!!")
    }
    fmt.Println(names[index])
}

func handleOutOfBounds() {
    if r := recover(); r != nil {
        fmt.Println("Recovering from panic:", r)
    }
}

Output

Recovering from panic: Out of bound access for slice..!! Please verify index before access..!!
Exiting normally 

Run in playground

Explanation

The above example is quite the same as the previous example other than we have an additional function checkAndPrintWithRecover which contains the call toย 

  • defer function with recover which is handleOutOfBounds
  • callsย checkAndPrint function

So basically checkAndPrint function raises the panic but doesnโ€™t have the recover function instead call to recover lies in the checkAndPrintWithRecover function.

But still the program is able to recover from panicย  as panic can also be recovered in the called function also and subsequently in the chain as well.

I mentioned above that if the recover function is not within defer function then it will not stop the panic.

package main

import "fmt"

func main() {
    names := []string{"Ashok", "Sai", "Rama", "Seetha"}
    checkAndPrintWithRecover(names, 5)
    fmt.Println("Exiting normally")
}

func checkAndPrintWithRecover(names []string, index int) {
    handleOutOfBounds()
    checkAndPrint(names, 5)
}

func checkAndPrint(names []string, index int) {
    if index > (len(names) - 1) {
        panic("Out of bound access for slice..!! Please verify index before access..!!")
    }
    fmt.Println(names[index])
}

func handleOutOfBounds() {
    if r := recover(); r != nil {
        fmt.Println("Recovering from panic:", r)
    }
}

Output

panic: Out of bound access for slice..!! Please verify index before access..!!

goroutine 1 [running]:
main.checkAndPrint(0xc000068f38, 0x4, 0x4, 0x5)
    /tmp/sandbox944161862/prog.go:16 +0xea
main.checkAndPrintWithRecover(0xc000068f38, 0x4, 0x4, 0x5)
    /tmp/sandbox944161862/prog.go:12 +0x50
main.main()
    /tmp/sandbox944161862/prog.go:7 +0xcc

Run in playground

In the above example, theย recover functionย  is not within defer function.ย  as you can see from the output that it does not stop panic and henceย  you see the above output.

Recover in Go


Scroll to top