DEV Community

Cover image for Go Control Flow - Deferring Panic with Recovery
Adjie Djaka Permana
Adjie Djaka Permana

Posted on

Go Control Flow - Deferring Panic with Recovery

#go

Pre-requisite


  • I assume that you must have understood the basic of Go Lang.
  • I assume that you must have understood how basic control flow, like if-statement, switch, and loop works.
  • I advice you to clone my repository.
git clone https://github.com/codewithdjaeger/go-with-golang.git

cd go-with-golang/control-flow
Enter fullscreen mode Exit fullscreen mode

Control Flow


Technically, control flow is the order in which individual statements, instructions, or function calls of an imperative program are executed or evaluated. Simply, it is the order of the lines of the code which contains either functions or statements that will be executed.

Control Flow

The most common control flow in almost every language is if-statement, switch, and looping. But, Go also has its signature control flow and they are defer, panic, and recovery. Usually, they're used to handling the error that is occurred in the control flow.

Defer the Flow Deferrer


defer, as its name, it is a keyword that will postpone the execution of a function calls until before the function in which the defer statement is located returns. Simply, any function calls that is preceded by the defer keyword will be fired immediately before the function returns. To make it clearer, take a look at the code below.

package main

import "fmt"

func main() {
    defer fmt.Println("This will be printed at the end")
    fmt.Println("This will be printed at the beginning")
}

// Output : 
// This will be printed at the beginning
// This will be printed at the end

Enter fullscreen mode Exit fullscreen mode

Just for you know, defer only handle function calls. Otherwise, it will raise an error.

Usually, defer is used to help you handle the task that you might forget, like closing a file. For more advanced uses, it will help you to deal with the error. But, we will discuss this later. For now, take a look at the code below to see the example of defer use to close the file.

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() // Close response body
    robots, err := ioutil.ReadAll(res.Body) // Read response body
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(robots)
}
Enter fullscreen mode Exit fullscreen mode

As you can see in the code above, we have close the response body before the program reads it. Normally, it will cause the program to be terminated because the program trying to read on a closed response body. But, thanks to defer, the closing of the response body will be postponed until before the main function reaches its return.

So, what if we precede defer in all function calls in order?

Theoretically, the program will scan and execute all deferred function calls from the last order and it will result in the LIFO (Last In First Out) output.

package main

import "fmt"

func main() {
    defer fmt.Println("This will be printed last")
    defer fmt.Println("This will be printed second")
    defer fmt.Println("This will be printed first")
}

// Output : 
// This will be printed first
// This will be printed second
// This will be printed last
Enter fullscreen mode Exit fullscreen mode

Panic, Makes Me Panic


Exception in Go is called panic, it can either be thrown by runtime or forced by programmer to handle the worse case. It will terminate current running control flow like the example below.

package main

import "fmt"

func main() {
    fmt.Println("Get ready!")
    panic("LET'S PANIC!!!")
    fmt.Println("This will not be executed")
}

// Output :
// Get ready!
// panic: LET'S PANIC!!!

// goroutine 1 [running]:
// main.main()
//        D:/Code/learn/learn-golang/control flow/panic.go:7 +0xa5
// exit status 2
Enter fullscreen mode Exit fullscreen mode

Also, panic will fire all defer function calls that is declared before the panic within the same function.

package main

import "fmt"

func main() {
    defer fmt.Println("This will be printed immediately when panic occurred!")
    fmt.Println("Get ready!")
    panic("LET'S PANIC!!!")
}

// Output :
// Get ready!
// This will be printed immediately when panic occurred!
// panic: LET'S PANIC!!!

// goroutine 1 [running]:
// main.main()
//         D:/Code/learn/learn-golang/control flow/panicDefer.go:8 +0x105
// exit status 2
Enter fullscreen mode Exit fullscreen mode

From the code above, we've known that defer will still be executed despite the program is terminated by panic. It explains why defer can be used to deal with errors. But, How? We will cover this in the recover function section.

Recover Will Do the Recovery


recover helps you to regain control after panicking. It's really useful to clean up the mess caused by the error and prevent your program from just terminating without closing or doing some clean-up task.

To do it, we can combine the uses of defer and recover like the example below.

package main

import (
    "fmt"
    "log"
)

func recoveryFunction() {
    if err := recover(); err != nil {
        log.Println("Program Recovered from", err)
    }
}

func panicFunction() {
    defer recoveryFunction()
    fmt.Println("Get Ready!")
    panic("PANIC!!!!")
    fmt.Println("Panic End")
}

func main() {
    fmt.Println("Program Initialized")
    panicFunction()
    fmt.Println("Program end")
}

// Output :
// Program Initialized
// Get Ready!
// 2021/08/02 13:56:36 Program Recovered from PANIC!!!!
// Program end
Enter fullscreen mode Exit fullscreen mode

In the above example, we defer function that contains recover function. From there, we see that the program doesn't throw something like the output below and also does not terminate the program.

// goroutine 1 [running]:
// main.main()
//         D:/Code/learn/learn-golang/control flow/panicDefer.go:8 +0x105
// exit status 2
Enter fullscreen mode Exit fullscreen mode

Instead, it prints a log of what happened and still runs the program until its end. It is good because, in the real world, the program is expected not to be terminated/exit and left the process unfinished when the error occurred.

Conclusions


  • Control flow is the order of lines of code that will be executed.
  • In Go, there are 3 signatures part of error handling, it is defer, panic, and recover.
  • defer will Postpone the execution of function calls until before the function return.
  • panic can either be thrown by runtime error or forced with custom error.
  • recover can be used to determine how the error will be handled instead of terminating the program.

Discussion (0)