Menu Close

Go – defer, panic and recover in go golang

In this article, we are going to learn about panic, defer and recover in go golang.

This article covers definition, syntax and code in detail for panic, defer and recover in go golang.

Panic:

It is a built-in function in go golang. panic is similar to throwing exception like other language. When a function encounters a panic, it stops execution. If there is any deferred function, then it gets executed and then control returns to its caller.

This process continues until all the functions of the current goroutine have returned at which point the program gets the panic message.

A panic is caused either by a runtime error, or an explicit call to the built-in panic function.

It is possible to regain control of a panicking program using recover.

You can relate panic and recover to trycatchfinally in other programming language. 

When to use panic statement in go golang:

We should use panic and recover mechanism where the program just cannot continue execution anymore.  Otherwise we should use errors wherever possible.

Syntax of panic function:

func panic(interface{})  

The argument passed to panic function will be printed when the program terminates. 

Panic example with code:

package main

import ( 
 
    "fmt"

)

func student_detail(firstName *string, lastName *string) {  

    if firstName == nil {

        panic("runtime error: Student’s first name cannot be nil")

    }

    if lastName == nil {

        panic("runtime error: Student’s last name cannot be nil")

    }

    fmt.Printf("%s %s\n", *firstName, *lastName)

    fmt.Println("returned normally from fullName")

}

func main() { 
 
    firstName := "john"

    student_detail(&firstName, nil)

    fmt.Println("returned normally from main")

}

Output:

panic: runtime error: Student’s last name cannot be nil 

goroutine 1 [running]: 
main.student_detail(0x41a7a0, 0x0)           
    /tmp/sandbox130128312/prog.go:19 +0x1a0 
main.main() 
    /tmp/sandbox130128312/prog.go:33 +0x40

Analysis of above panic program:

The above program is to print full name of a student. This function checks whether firstName and lastName pointers are nil in line nos. 11 and 17 respectively. If it is nil, then the function calls panic function.

So once student_detail function is called, it gets panic at line number 19. It prints panic function message i.e “panic: runtime error: student’s last name cannot be nil “ and then prints the stack trace.

Defer:

When a function encounters a panic. its execution is stopped and then defer functions are executed and then the control returns to its caller.

In Leyman term, Defer generally used to cleanup resources like file, database connection etc. Multiple defer functions are pushed into stack and executes in Last In First Out (LIFO) order. It is useful to catch errors.

Defer statement gets called right after return of the function. 

Defer example with code:

1:)

In the given below example, function opens two files and copies the content of one file into another. 

func copy_file_content(dstFileName, srcFileName string) (written int64, err error) {

    src_file, error := os.Open(srcFileName)

    if error != nil {

        return

    }
 
    dst_file, error := os.Create(dstFileName)

    if error != nil {

        return

    }

    written, error = io.Copy(dst_file, src_file)

    dst_file.Close()

    src_file.Close()

    return
}

This function works fine but there is an issue. If the call to os.Create fails, the function will return without closing the source file. We can solve this problem by putting src.Close() before second return statement. 

But just think, when the function is more complex . It would be very difficult to find and resolve this kind of issues. We can overcome such type of issues by using defer statement. 

2:)

package main

import (

    "fmt"

)

func student_detail(firstName *string, lastName *string) { 

    defer fmt.Println("deferred call in fullName")

    if firstName == nil {

        panic("runtime error: Student’s first name cannot be nil")

    }

    if lastName == nil {
 
        panic("runtime error: Student’s last name cannot be nil")

    }

    fmt.Printf("%s %s\n", *firstName, *lastName)

    fmt.Println("returned normally from fullName")

}

func main() {  

    defer fmt.Println("deferred call in main")

    firstName := "john"

    student_detail(&firstName, nil)

    fmt.Println("returned normally from main")

}

Output:

deferred call in fullName 
deferred call in main 
panic: runtime error: Student’s last name cannot be nil 

goroutine 1 [running]: main.student_detail(0x41a798, 0x0)
     /tmp/sandbox484868770/prog.go:21 +0x280
main.main() 
    /tmp/sandbox484868770/prog.go:37 +0xc0

3:)

package main

import (

    "fmt"

)

func main() {  

    defer func() {
		fmt.Println("Taking care of cleaning process!")
	}()

	defer func() {
		fmt.Println("Closing opened file handlers")
	}()

	defer func() {
		fmt.Println("Closing opened database connection")
	}()

	defer func() {
		fmt.Println("Recover error if occurred.")
	}()

	fmt.Println("Doing some complex calculation, remote http call and database call!")
}

Output:

Doing some complex calculation, remote http call and database call! 
Recover error if occurred. 
Closing opened database connection 
Closing opened file handlers 
Taking care of cleaning process!

4:)  As we know defer push statements into stack in a LIFO order. To understand more , please check given below program.

package main

func main() {  

    for i := 0; i < 7; i++ {
              defer println(i)
       }
}

Output:

6
5
4 
3 
2 
1 
0

Recover:

Recover is a built-in function. A program recovers from a panic by calling the built-in function recover, after which normal execution is resumed. 

It is used to regain control after a panic. it used with defer statement to recover panic in goroutine.

Syntax:

func recover() interface{}  

Example with code:

package main

import (

    "fmt"

)

func main() {

	panicWithRecover()
	
	fmt.Println("I need to run server at any cost!")
	
}

func panicWithRecover() {

	defer func() {
	
		if err := recover(); err != nil {
		
			fmt.Println(err)
			
		}
		
	}()
	
	panic("Unable to connect server!")
}

Output:

Unable to connect server!
I need to run server at any cost!
 
Reference:
https://blog.golang.org/defer-panic-and-recover
https://golangbot.com/panic-and-recover/

To learn more about golang, Please follow given below link:
https://www.techieindoor.com/go-lang-tutorial/
Posted in golang

Leave a Reply

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