defer
: Function B called with the defer keyword in function A will be called in function Areturn
After the implementation.
Take a look at a basic example to see what defer looks like
func main(a) {
fmt.Println("in main func:", foo())
}
func foo(a) int {
i := 0
defer fmt.Println("in defer :", i)
i = 1000
fmt.Println("in foo:", i)
return i+24
}
Copy the code
This code will be printed when it runs
in foo: 1000
in defer : 0
in main func: 1024
Copy the code
The variable I is initialized to 0, and defer specifies that the fmt.println function is deferred until the return, and finally the main function calls foo to print the return value.
What is it for?
Many variable resources are declared in the function, and when the function ends, we usually do something with them: destroy them, release them (for example, database links, file handles, streams).
Normally, we deal with these things before the return statement.
However, if a function contains multiple returns, we need to perform these operations once before each return, which often results in omissions and cumbersome code maintenance.
For example, instead of defer, the code might look like this:
func foo(i int) int {
if i > 100 {
fmt.Println("Not the expected number.")
return 0
}
if i < 50 {
fmt.Println("Not the expected number.")
return 0
}
return i
}
Copy the code
After using defer, the code looks like this
func foo(i int) int {
defer func(a) {
fmt.Println("Not the expected number.")
}()
if i > 100 {
return 0
}
if i < 50 {
return 0
}
return i
}
Copy the code
What is the order in which multiple defers are executed in a function?
Defer can be used multiple times in the same function.
The multiple deferred functions specified a “first in, last out” sequence.
Why is that?
Think of it this way: the defer keyword causes the following code to execute before the functions it specifies, including the defer statement, and so on.
This order is necessary because in the function, the objects defined later may depend on the previous objects, or if the first defer executes, it is likely to cause an exception when the second defer executes.
As a result, the Go language designed Defer in a first in, last out order.
Example:
func foo(a) {
i := 0
defer func(a) {
i--
fmt.Println("The first defer", i)
}()
i++
fmt.Println("+1 I:", i)
defer func(a) {
i--
fmt.Println("Second defer", i)
}()
i++
fmt.Println("+1 I:", i)
defer func(a) {
i--
fmt.Println("The third defer", i)
}()
i++
fmt.Println("+1 I:", i)
}
Copy the code
You can see it after you run it
After +1 I: 1, after +1 I: 2, after +1 I: 3, after +1 I: 3, after +1 I: 3, after +1 I: 3, after +1 I: 3, after +1 I: 3, after +1 I: 3, after +1 I: 3Copy the code
This process shows how, after the function executes, we defer in and out and process the variables step by step.
When the argument is passed to the function specified by defer, what will the argument value be?
There are some generalizations on the web that say that the parameters of the function you specify to defer are determined when you defer, but this is just a summary because the Go language passes values except for Map, slice, and chan.
Modify the above example
func foo(a) {
i := 0
defer func(k int) {
fmt.Println("The first defer", k)
}(i)
i++
fmt.Println("+1 I:", i)
defer func(k int) {
fmt.Println("Second defer", k)
}(i)
i++
fmt.Println("+1 I:", i)
defer func(k int) {
fmt.Println("The third defer", k)
}(i)
i++
fmt.Println("+1 I:", i)
}
Copy the code
The results obtained
After +1 I: 1, after +1 I: 2, after +1 I: 3, after +1 I: 3, after +1 I: 3, after +1 I: 3, after +1 I: 3, after +1 I: 3, after +1 I: 3, after +1 I: 3Copy the code
Some people might think it’s a little unexpected, isn’t I already computed to 3 when I return? Why is I not 3 in the function specified by deferred?
The function specified by the defer keyword is executed after return, which makes it easy to imagine calling the function after return.
However, the function that defer specified was called on the current line, deferred to return, not “moved” to return, so the value of the current parameter is passed when called.
What happens when you pass a pointer argument?
If you want the value of the function argument that defer specified to be processed by the code that follows, you can pass the pointer argument to the function that defer specified.
Modify the code:
func foo(a) {
i := 0
defer func(k *int) {
fmt.Println("The first defer", *k)
}(&i)
i++
fmt.Println("+1 I:", i)
defer func(k *int) {
fmt.Println("Second defer", *k)
}(&i)
i++
fmt.Println("+1 I:", i)
defer func(k *int) {
fmt.Println("The third defer", *k)
}(&i)
i++
fmt.Println("+1 I:", i)
}
Copy the code
After running
After +1 I: 1, after +1 I: 2, after +1 I: 3, after +1 I: 3, after +1 I: 3, after +1 I: 3Copy the code
Does defer affect the return value?
As you can see in the first example at the beginning, defer was executed after foo and before the return value was printed in main, but it didn’t affect the print in main.
This is because the same principles apply to Go except for map, Slice, and chan
Compare the results of the foo1 and foo2 functions:
func main(a) {
fmt.Println("foo1 return :", foo1())
fmt.Println("foot return :", foo2())
}
func foo1(a) int {
i := 0
defer func(a) {
i = 1} ()return i
}
func foo2(a) map[string]string {
m := map[string]string{}
defer func(a) {
m["a"] = "b"} ()return m
}
Copy the code
After running, print out
foo1 return : 0
foot return : map[a:b]
Copy the code
The difference between the two functions is the type of value that is returned. In foo1, after an int return, defer does not affect the return, but in foo2, map is passed by reference, so defer changes the return.
This shows that, except for map, slice, and chan, the values are copied into a temporary variable space when we return, so what we do to the variables in the function we defer specified will not affect the return result.
In another case, the variable name is declared for the return value of the function. In this case, the variable space is declared before the function executes and only the contents of the variable space are returned when the function returns, so defer can change the return value.
For example, modify the foo1 function to declare a variable name I for its return value:
func foo1(a) (i int) {
i = 0
defer func(a) {
i = 1} ()return i
}
Copy the code
Run it again and you can see:
foo1 return : 1
Copy the code
The return value has been modified by the function specified by defer.
The use of defer in panic and Recover processing
In the Go language, one classic usage scenario for defer is Recover.
During function execution, there may be panic in many places. If we do not call recover after panic, the program will exit. In order to prevent the program from exiting, we need to call recover after Panic, but the code after panic will not be executed. However, the function named by defer can be executed within the function named by panic, so recover can only be called in the function named by defer, and only needs to be processed in the one function named by defer.
Such as:
func panicfunc(a) {
defer func(a) {
fmt.Println("before recover")
recover()
fmt.Println("after recover")
}()
fmt.Println("before panic")
panic(0)
fmt.Println("after panic")}Copy the code
After running, print:
before panic
before recover
after recover
Copy the code
Summary of the following
- The defer statement is very important and often used and must be mastered
- Handle multiple in unity
return
andpanic/recover
Use defer in the scenario - Keep in mind the important rule that function parameters in Go pass values (except map, Slice, and chan), and properly evaluate the function parameter values that defer specifies
- Defer does not affect the return value unless it is Map, Slice, or chan, or the return value defines the variable name
- Execution order: First in, last out
Welcome to pay attention to the public number, and we learn programming together