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
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.
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
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)
}
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
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
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
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
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
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
, andrecover
. -
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.
Top comments (0)