Advertisement

In this blog, we will be looking at another kind of control flow keywords in Golang like Defer, Panic, and Recover.

What we will learn?

  • Defer, Panic and Recover in Golang
    • What are the Defer, Panic, and Recover keywords in Golang?
    • Why Golang Defer, Panic, and Recover keywords are used?
    • Important use-cases of Defer, Panic, and Recover keywords in Golang?

Before learning Defer, Panic, and Recover in Golang must learn the Golang Basics!

Golang Defer

In a Golang Program, the execution is from the top to the bottom in a function. If we need to skip certain statements or logic, we use Control Flow like if-else and switch statements, to skip or add statements based on a condition.

One kind of control flow keyword is “defer” in Golang.

Golang Defer Syntax

If you want to make the execution of a Statement defer in Golang just precede the statement with a “defer” Keyword.

Syntax:

defer fmt.Println(“Hello World!”)

Golang Defer Example

	fmt.Println("First")
	defer fmt.Println("Second")
	fmt.Println("Third")

Output:

First
Third
Second

The Execution of the three statements starts from the top to bottom in Golang, but the second print statement executes last. but why?

This is because we have used defer in-front of the print statement which delays the execution of the statement till that function returns.

	a := "Golang Defer, Panic and Recover"
	defer fmt.Println(a)
	a = "Control Flow"

Output:

Golang Defer, Panic and Recover

Golang Defer’s keyword stores the argument on which point of time it is called. Change in the argument doesn’t affect the defer statement’s argument.

Golang Defer Stack

The defer functions are stored in a stack and thus maintains LIFO order i.e Last-in, First-Out.

Let’s understand Golang Defer Stack by an example:

In the previous example, the defer keyword was used on the second statement but what if the defer keyword is used with every statement. will there be any change in the output?

	defer fmt.Println("First")
	defer fmt.Println("Second")
	defer fmt.Println("Third")

Output:

Third
Second
First

This time the output is reverse of the execution control flow.

This happens because each time the defer keyword is used with any statement, the function is stored in a stack, and Stack follows the LIFO order, and thus the last function to enter into the stack gets executed first.

Golang defer stack
Golang Defer Stack | Source- Divyanshu Shekhar

The image shows a defer stack which stores the deferred statements in a stack and executes it when the function is about to return.

Stack follows LIFO order i,e Last-in, First-out and thus execution is like:

Golang defer Stack LIFO
Golang Defer Execution Order (LIFO)

Golang Defer Assignment

In Golang Functions calls can also be deferred.

Note: Function calls are deferred in GO and not Functions.

Example:

package main

import (
	"fmt"
)

func test() {
	fmt.Println("Testing Golang defer Function")
}

func main() {

	fmt.Println("Start")
	defer test()
	fmt.Println("Third")

}

Output:

Start
Third
Testing Golang defer Function

Golang Defer Anonymous Function

An anonymous function is a function without a name, this type of function is called only once as they are not identified by any kind of name to call it elsewhere.

When deferring an anonymous function it must be called at the same time as only function calls are deferred in Golang and not functions.

Example of Golang defer Anonymous Function:

package main

import (
	"fmt"
)

func main() {

	fmt.Println("Start")
	defer func() {
		fmt.Println("Golang Defer Anonymous Function")
	}()
	fmt.Println("Third")

}

Output:

Start
Third
Golang Defer Anonymous Function

Golang Defer Use-Case

The Golang Defer Keyword is mainly used to remove the resource used by the Golang program.

Let’s see a practical Example:

Reading robots.txt file from google.com/robots.txt.

package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
)

func main() {

	res, err := http.Get("http://www.google.com/robots.txt")
	if err != nil {
		log.Fatal(err)
	}
        defer res.Body.Close()
	robots, err := ioutil.ReadAll(res.Body)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%s", robots)

}

The Output is Google robots.txt.

Programs like these where the use of the resource is made and it’s important to close the resource after its use. But many times developers forget to release the resource and thus bug is introduced in their program.

Defer Keyword helps us to get rid of this bug. Here Opening and Closing of the resource is done immediately and the resource is used afterward.

Now the question arises How the program will use the resource if it will be closed immediately.

The answer is the resource closing statement is preceded by defer keyword which helps the statement to execute when the function is abour to return.

This also helps in Golang’s memory management.

Golang Panic

The Go Language doesn’t have exceptions like other languages ( C, C++, Java, Python, etc.). Golang doesn’t consider many cases as exceptions like other languages treats, these cases are considered normal in Golang and the choice is left upon the developer to either make it an exception or not.

Golang Panic Example

	a, b := 5, 0
	div := a / b

	fmt.Println(div)

panic: runtime error: integer divide by zero

goroutine 1 [running]:
main.main()
/main.go:9 +0x11 // This is Golang panic stack trace, where error has occoured.
exit status 2

Golang Explicit Panic

Golang Panic Keyword is used to make Panic in a program and stop the execution. That’s why the third statement is not printed as soon as the panic keyword is encountered.

	fmt.Println("First")
	panic("Golang Panic")
	fmt.Println("Third")

Output:

First
panic: Golang Panic

goroutine 1 [running]:
main.main()
/main.go:10 +0x9c
exit status 2

Golang Panic Usecase

As we all know Golang hasn’t included exceptions in this language so the developers need something to throw exceptions or to stop execution of the application whenever a developer needs to do so.

Let’s see a Practical Example:

Creating a Golang server to listen on a port to handle responses and requests.

package main

import (
	"net/http"
)

func main() {

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("Hello GO World"))
	})
	err := http.ListenAndServe(":5000", nil)
	if err != nil {
		panic(err.Error())
	}

}

Output:

Golang Server
Golang Server | Source

When the port for Golang Server is already used by another application, the server Panics and stops the server.

Output when the port already in use:

panic: listen tcp :5000: bind: address already in use

Golang Defer, Panic and Recover

Let’s see all three / two in a single program.

Example 1:

	fmt.Println("Golang - The Go Language")
	defer fmt.Println("Golang defer, This is deferred!")
	panic("Golang Panic Example")
	fmt.Println("Third")

Output:

Golang – The Go Language
Golang defer, This is deferred!
panic: Golang Panic Example

In this case the deferred function is executed first and panic function after that, this makes an order of execution.

Golang execution order
Golang Execution Order | Source – Divyanshu Shekhar

This order is preferred because the developers will not be happy if their program or application stops execution before closing all the resources the application was using.

And the closing of the resources is given to the defer keyword and thus they get called at first.

First the Resources are closed then the application stops the execution.

Golang Defer, Panic, and Recover in a Single Program.

The recover function will return nil if the program is not under panic but if under panic it will return the panic error.

This doesn’t stops the program but logs the error and skips the part after the panic has Occurred.

package main

import (
	"fmt"
	"log"
)

func main() {

	fmt.Println("Start")
	defer func() {
		if err := recover(); err != nil {
			log.Println("Error:", err)
		}
	}()
	panic("Golang Panic Occurred!")
	fmt.Println("end")

}

Output:

Start
2020/05/09 17:40:03 Error: Golang Panic Occurred!

When used recover function inside a function, tells the real story.

Let’s see!

package main

import (
	"fmt"
	"log"
)

func test() {
	fmt.Println("Panic Start")
	defer func() {
		if err := recover(); err != nil {
			log.Println("Error:", err)
		}
	}()
	panic("Golang Panic Occoured!")
	fmt.Println("This will not be executed.")
}

func main() {

	fmt.Println("Start")
	test()
	fmt.Println("end")

}

Output:

Start
Panic Start
2020/05/09 17:37:59 Error: Golang Panic Occoured!
end

The test function uses recover function in order to detect any panic. when the panic is detected the recover function takes the error message logs it onto the console and doesn’t let program to stop executing which otherwise would have stopped after encountering panic.

I hope you all have learnt something from this blog.

Also, read Why Golang is called the future of server-side language?

Learn about Golang Defer, Panic, and recover in official Golang Docs.

blog.golang.org

About Author
0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments
Scroll to Top