Panic and recover in Golang [Best Go examples 2020]

As we have seen in this panic explanation, when a program panics, it gets terminate in the middle and it does not go for complete execution. Let’s try the same aboutme program and here we will call the same aboutme() function for 3rd time as below.

package main
import “fmt”

func aboutme(name *string, age int) {
        if name == nil {
                panic(“runtime error: name cannot be nil”)
        }
        fmt.Printf(“My name is %s and my age is %d\n”, *name, age)
}

func main() {
        name1 := “SUSANTA”
        name2 := “PRASANTA”
        aboutme(&name1, 10)
        aboutme(nil, 20)
        aboutme(&name2, 30)
        fmt.Println(“returned normally from main”)
}

My name is SUSANTA and my age is 10
panic: runtime error: name cannot be nil

goroutine 1 [running]:
main.aboutme(0x0, 0x14)
    /tmp/sandbox112844930/prog.go:6 +0x111
main.main()
    /tmp/sandbox112844930/prog.go:15 +0x7b

As above we can see the first time call of aboutme() with argument &name1 is success and it prints in output like “My name is SUSANTA and my age is 10”. When called 2nd time with nil, the program gets panic and it gets terminate with the stack trace. Hence the third time call of aboutme() is not executed.

build in recover() function:-

There is a build in function called recover(), using which we can recover from a panic function and thus it will help to execute the complete program though there is a panic.
Lets modify the above program and see how it works.

package main
import “fmt”

func myrecover() {
        r := recover()
        if r != nil {
                fmt.Println(“RECOVERED FROM@@@@@ “, r)
        }
}

func aboutme(name *string, age int) {
        defer myrecover()
        if name == nil {
                panic(“runtime error: name cannot be nil”)
        }
        fmt.Printf(“My name is %s and my age is %d\n”, *name, age)
}

func main() {
        name1 := “SUSANTA”
        name2 := “PRASANTA”
        aboutme(&name1, 10)
        aboutme(nil, 20)  // ======= Line-X
        aboutme(&name2, 30)  // ======= Line-Y
        fmt.Println(“returned normally from main”)
}

My name is SUSANTA and my age is 10
RECOVERED FROM@@@@@  runtime error: name cannot be nil.
My name is PRASANTA and my age is 30
returned normally from main

Here we can see, when we have called the aboutme() for 2nd time at Line-X, with nil argument, the function has got a panic but the program doesn’t terminate as we have used the build-in recover() function. Hence the program has recovered completely from a panic and next it is able to execute the aboutme() at line-Y properly and normally comes out of the program.

panic, reciver and gorouten:—

Recover function doesnt work when the panic happens at different goroutine. Lets try this example. Hope you have gone through this goroutine and channel blog for better understanding.

package main

import “fmt”

func myrecover() {
        r := recover()
        if r != nil {
                fmt.Println(“RECOVERED FROM@@@@@ “, r)
        }
}

func aboutme(name *string, age int) {
        defer myrecover()
        fmt.Printf(“In function aboutme\n”)
        ch := make(chan bool)
        go aboutme_2(name, age, ch)  //====== Line-X
        <-ch
}

func aboutme_2(name *string, age int, ch chan bool) {
        if name == nil {
                panic(“runtime error: name cannot be nil”)
        }
        fmt.Printf(“My name is %s and my age is %d\n”, *name, age)
        ch <- true
}

func main() {
        name1 := “SUSANTA”
        name2 := “PRASANTA”
        aboutme(&name1, 10)
        aboutme(nil, 20)
        aboutme(&name2, 30)
        fmt.Println(“returned normally from main”)
}

In function aboutme
My name is SUSANTA and my age is 10
In function aboutme
panic: runtime error: name cannot be nil

goroutine 7 [running]:
main.aboutme_2(0x0, 0x14, 0xc00005e0c0)
    /tmp/sandbox230587611/prog.go:21 +0x12e
created by main.aboutme
    /tmp/sandbox230587611/prog.go:15 +0xdd

As above we can see, in line-X, we have a goroutine ( go aboutme_2() ), which is having a build-in panic() function, but the program is not able to recover, as its using a goroutine. Instead goroutine if we will make it a normal function, it will work fine.

package main
import “fmt”

func myrecover() {
        r := recover()
        if r != nil {
                fmt.Println(“RECOVERED FROM@@@@@ “, r)
        }
}

func aboutme(name *string, age int) {
        defer myrecover()
        fmt.Printf(“In function aboutme\n”)
        aboutme_2(name, age) //===== Line-X
}

func aboutme_2(name *string, age int) {
        if name == nil {
                panic(“runtime error: name cannot be nil”)
        }
        fmt.Printf(“My name is %s and my age is %d\n”, *name, age)
}

func main() {
        name1 := “SUSANTA”
        name2 := “PRASANTA”
        aboutme(&name1, 10)
        aboutme(nil, 20)
        aboutme(&name2, 30)
        fmt.Println(“returned normally from main”)
}

In function aboutme
My name is SUSANTA and my age is 10
In function aboutme
RECOVERED FROM@@@@@  runtime error: name cannot be nil
In function aboutme
My name is PRASANTA and my age is 30
returned normally from main

Here in line-X, instead of goroutine, we have used it as a normal go function and we can see the program has recovered completely from a panic and it executes normally.

panic, recover and stacktrace:-

Till now the panic and recover programs we have executed, we are not seeing the stack trace. There is a way. By using debug.PrintStack(), you can print the stack trace. Check below.

package main
import (
        “runtime/debug”
        “fmt”
)

func myrecover() {
        r := recover()
        if r != nil {
                fmt.Println(“RECOVERED FROM@@@@@ “, r)
                debug.PrintStack()
        }
}

func aboutme(name *string, age int) {
        defer myrecover()
        if name == nil {
                panic(“runtime error: name cannot be nil”)
        }
        fmt.Printf(“My name is %s and my age is %d\n”, *name, age)
}

func main() {
        name1 := “SUSANTA”
        name2 := “PRASANTA”
        aboutme(&name1, 10)
        aboutme(nil, 20)
        aboutme(&name2, 30)
        fmt.Println(“returned normally from main”)
}

My name is SUSANTA and my age is 10
RECOVERED FROM@@@@@  runtime error: name cannot be nil
goroutine 1 [running]:
runtime/debug.Stack(0x37, 0x0, 0x0)
    /usr/local/go-faketime/src/runtime/debug/stack.go:24 +0x9f
runtime/debug.PrintStack()
    /usr/local/go-faketime/src/runtime/debug/stack.go:16 +0x25
main.myrecover()
    /tmp/sandbox030754585/prog.go:12 +0xb4
panic(0x4a6920, 0x4dcf50)
    /usr/local/go-faketime/src/runtime/panic.go:969 +0x175
main.aboutme(0x0, 0x14)
    /tmp/sandbox030754585/prog.go:19 +0x155
main.main()
    /tmp/sandbox030754585/prog.go:28 +0x7b
My name is PRASANTA and my age is 30
returned normally from main

2 thoughts on “Panic and recover in Golang [Best Go examples 2020]

Leave a Reply

Your email address will not be published. Required fields are marked *