官术网_书友最值得收藏!

Panics, recovers, and defers

In Go, there is a special built-in function called panic. When you invoke panic in your code, your program is interrupted, and a panic message is returned. If a panic gets triggered and you don't capture it in time, your program will stop execution and will exit, so be very careful when you use a panic. Here is a code example:

func panicTest(p bool) {
if p {
panic("panic requested")
}
}

In the preceding example, we wrote a function that checks a flag, p. If p is true, then we throw a panic. The argument to the panic function is the message that wants the panic to return. Here is a more complete program that you can run in Go's playground (http://play.golang.org):

package main

import "fmt"

func main() {
panicTest(true)
fmt.Println("hello world")
}

func panicTest(p bool) {
if p {
panic("panic requested")
}
}

When I executed that code from the main function in Go's playground (http://play.golang.org), I got the following error:

panic: panic requested

goroutine 1 [running]:
main.panicTest(0x128701, 0xee7e0)
  /tmp/sandbox420149193/main.go:12 +0x60
main.main()
  /tmp/sandbox420149193/main.go:6 +0x20

The panic caused the program to be terminated, which is why hello world was never printed. Instead, we got the panic message.

So, now that we understand how panics work, an obvious question arises—how do we capture a panic and prevent it from killing our program?

Before we answer that question, we first need to introduce the concept of defer. The defer keyword can be used to indicate that a piece of code must only be executed after the surrounding function returns. As always, this will make much more sense after we look at a code example:

func printEnding(message string) {
fmt.Println(message)
}

func doSomething() {
//In here we use the keyword "defer"
//This will call printEnding() right after doSomething()

defer printEnding("doSomething() just ended")

//In here, we just print values from 0 to 5
for i := 0; i <= 5; i++ {
fmt.Println(i)
}
}

In the preceding code, when we made use of defer, we effectively asked for the printEnding() function to be executed right after  doSomething() finishes its execution.

The defer statement basically pushes a function call to a list, and the list of saved calls is executed after the surrounding function returns. Defer is most commonly used to clean up resources, like closing a file handler, for example.

Here is the full version of the preceding program:

package main

import (
"fmt"
)

func main() {
doSomething()
}

func printEnding(message string) {
fmt.Println(message)
}

func doSomething() {
defer printEnding("doSomething() just ended")
for i := 0; i <= 5; i++ {
fmt.Println(i)
}
}

And here is the output of that program:

0
1
2
3
4
5
doSomething() just ended

Now, what if we put defer multiple times in our function?

package main

import (
"fmt"
)

func main() {
doSomething()
}

func printEnding(message string) {
fmt.Println(message)
}

func doSomething() {
defer printEnding("doSomething() just ended 2")
defer printEnding("doSomething() just ended")
for i := 0; i <= 5; i++ {
fmt.Println(i)
}
}

The defer statements typically enter a stack data structure, which means they execute based on the first-in-last-out rule. So, this basically means that the first defer statement in the code will execute last, while the next one will execute right before it and so on. To paint a clearer picture, let's look at the program's output:

0
1
2
3
4
5
doSomething() just ended
doSomething() just ended 2

Perfect! We are now ready to answer our earlier question—how can we capture and handle a panic before it terminates our program? We now know about defer and how it ensures that a piece of code of our choosing gets executed right after the surrounding function exits. So, defers can definitely be used to insert a piece of code after a panic occurs, but are defers enough? The answer is no—there is a built-in function known as recover() that we can use to capture a panic and return the panic's message.

Again, a code snippet is worth a thousand words:

package main

import "fmt"

func main() {
panicTest(true)
fmt.Println("hello world")
}

func checkPanic() {
if r := recover(); r != nil {
fmt.Println("A Panic was captured, message:", r)
}
}

func panicTest(p bool) {
// in here we use a combination of defer and recover
defer checkPanic()
if p {
panic("panic requested")
}
}

The preceding code will produce the following output:

A Panic was captured, message: panic requested
hello world

As you can see, we utilized a combination of defer and the recover() function to capture the panic to prevent it from terminating our program. If no panic occurred, the recover() function will return nil. Otherwise, the recover() function will return the error value of the panic. If we use recover() alone, it won't be effective without being combined with defer.

主站蜘蛛池模板: 白水县| 始兴县| 偏关县| 洪雅县| 同江市| 启东市| 江口县| 三台县| 水富县| 马公市| 伊宁县| 通海县| 武穴市| 昭平县| 荔浦县| 留坝县| 青神县| 桦甸市| 融水| 栾川县| 博爱县| 翼城县| 清徐县| 海阳市| 睢宁县| 泰州市| 仙桃市| 柳河县| 丹巴县| 农安县| 全椒县| 珠海市| 天镇县| 曲阜市| 大化| 怀远县| 应城市| 葵青区| 亳州市| 黄山市| 三门县|