The usage rules of panic and recover in golang

There is no tye...catch exception handling logic in golang. Use defer, panic and recover in golang to control the program execution flow, so as to achieve the purpose of handling exceptions.

Panic is a built-in function that stops the flow of program execution. Assuming that some code in the current F function triggers the panic function, the F function stops the execution of the following code, and then executes the defer function inside the F function (if the defer function has been declared...), then ends the F function, and the current The processing right is transferred to the calling function of F.

For the caller M of F, F ends by calling the panic function instead of executing return . This is important because calling the panic function ends with no return value.

For F's caller M, the F call is complete and has exited. So leads to the first rule:

After calling panic, the caller function execution exits from the current call site

We explain this problem with the following code:

package main

import "fmt"

func main() {
f()
fmt.Println("Returned normally from f.")
}

func f() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered in f", r)
}
}()
fmt.Println("Calling g.")
g(0)
fmt.Println("Returned normally from g.")
}

func g(i int) {
if i > 3 {
fmt.Println("Panicking!")
panic(fmt.Sprintf("%v", i))
}
defer fmt.Println("Defer in g", i)
fmt.Println("Printing in g", i)
g(i + 1)
}

In this function, when i is greater than 3, the panic function is triggered. Everything works fine until i is less than 3. Lead us to analyze the execution logic.

First of all, in f(), there is no exception before the g() function is called. So "Calling g." is output. Then start to call g(), when the input parameter is less than 3, it will always output "Printing in g", (Why not output "Defer in g", please refer to the previous article (rules of using defer)).

When i4, start to trigger the panic function and output Panicking!. It is necessary to remember that after a panic, the caller exits directly from the call site . Because g() is an iterative call, when i4 triggers a panic, the essence is the panic function triggered by the line g(i+1). Therefore, the subsequent functions of g() will not continue to execute, because there is a defer function, so three consecutive "Defer in g" are output.

At this point, the execution of the g(0) function is completed, and the f() function exits. Because there is a defer function in the f() function, the defer will be called to output "Recoverd in f".

So the final output will be:

Calling g.
Printing in g 0
Printing in g 1
Printing in g 2
Printing in g 3
Panicking!
Defer in g 3
Defer in g 2
Defer in g 1
Defer in g 0
Recovered in f 4
Returned normally from f.

After talking about the execution order, let's see why "Recovered in f 4" is output. This is the second rule:

Rule 2 The return value can be set through panic

The meaning of panic is not only to control the exception handling process, but also to return the cause of the exception. If the panic does not return the cause of the exception to the caller, the caller will have no way to deal with the problem. Therefore, when calling panic, generally speaking, a string is returned to indicate the reason for the failure. For example, "XXX value must not be greater than 3" in the above code.

The return value of panic is obtained through the recover function. The recover function is also a built-in function, which is specially used to receive the return value of the panic function. When the panic function is not called or has no return value , recover returns Nil.

When using the recover function, the following rule three is required:

Rule 3 The recover function will only be effective in the defer code block

Note that effective here does not mean execution. The implication is that the recover function can be executed anywhere, but only when it is executed in the defer code block can the purpose of handling exceptions be truly achieved.

We assume that the recover function is removed from the defer code block, as follows:

func f() int {

fmt.Println("Calling g.")
m := g(0)
r := recover()
if r != nil {
fmt.Println("Recovered in f", r)
}
fmt.Println("Returned normally from g.", m)
return m
}

Therefore, the recover function is outside, so the execution result is as follows:

Calling g.
Printing in g 0
Printing in g 1
Printing in g 2
Printing in g 3
Panicking!
Defer in g 3
Defer in g 2
Defer in g 1
Defer in g 0
panic: 4

goroutine 1 [running]:
[stack trace omitted]

It can be seen that the recover function does not take effect, but it is not executed, because according to rule 1, the current execution process will jump to the defer function. Therefore, only when the recover function is defined in defer will it be actually executed.

Then the question comes, which level of defer should the recover function be defined in. golang is searched step by step, and finally the main function will be found. If the defer in the main function does not have a recover function, golang will throw the final exception information as above.

There is not much content in this section, but I personally feel that it is still dry goods. After all, we cannot guarantee that the code we write will have no problems. It is not terrible to have problems, but the terrible thing is that the code is out of control. So learning defer, panci and recover well will help you write robust code.

Guess you like

Origin blog.csdn.net/qq_39312146/article/details/130443907