Defer has some rules, and if you don’t know them, the end result of your code implementation will be different from what you expect. Do you understand these rules?
test
This is the code used for defer, so consider the return value first.
package main
import (
"fmt"
)
/** * @author: Jason Pang * @description: Snapshot */
func deferFuncParameter1(a) {
var aInt = 1
defer fmt.Println(aInt)
aInt = 2
return
}
/** * @author: Jason Pang * @description: Snapshot */
func deferFuncParameter2(a) {
var aInt = 1
defer func(t int) {
fmt.Println(t)
}(aInt)
aInt = 2
return
}
/** * @author: Jason Pang * @description: Dynamic */
func deferFuncParameter3(a) {
var aInt = 1
defer func(a) {
fmt.Println(aInt)
}()
aInt = 2
return
}
/** * @author: Jason Pang * @description: Affects the return value * @return ret */
func deferFuncReturn1(a) (ret int) {
ret = 10
defer func(a) {
ret++
fmt.Println("-- -- -- -- --", ret)
}()
return 2
}
/** * @author: Jason Pang * @description: Does not affect the return value * @return ret */
func deferFuncReturn2(a) (ret int) {
ret = 10
defer func(ret int) {
ret++
fmt.Println("-- -- -- -- --", ret)
}(ret)
return 2
}
/** * @author: Jason Pang * @description: defer */
func deferFuncSeq1(a) {
var aInt = 1
defer fmt.Println(aInt)
aInt = 2
defer fmt.Println(aInt)
return
}
func main(a) {
fmt.Println("Snapshot")
deferFuncParameter1()
deferFuncParameter2()
deferFuncParameter3()
fmt.Println("Return value")
fmt.Println(deferFuncReturn1())
fmt.Println(deferFuncReturn2())
fmt.Println("Order of execution")
deferFuncSeq1()
}
Copy the code
The correct output is:
➜ myproject go run main.go
The snapshot
1
1
2
The return value
— — — — — 3
3
— — — — — 11
2
Execution order
2
1
Analysis of the
There are a few important rules to defer that can be found in the results above.
As soon as the rule defer is declared, its parameters are parsed in real time
When defer was declared, if the parameters were used directly, they would use the snapshot values and would not change throughout the lifetime. For example deferFuncParameter1 and deferFuncParameter2, the aInt changed after the defer declaration, but the value in defer remains the same.
func deferFuncParameter1(a) {
var aInt = 1
defer fmt.Println(aInt)
aInt = 2
return
}
func deferFuncParameter2(a) {
var aInt = 1
defer func(t int) {
fmt.Println(t)
}(aInt)
aInt = 2
return
}
Copy the code
On the other end of the spectrum is deferFuncParameter3, which changes with aInt.
func deferFuncParameter3(a) {
var aInt = 1
defer func(a) {
fmt.Println(aInt)
}()
aInt = 2
return
}
Copy the code
Rule 2 defer might manipulate the named return value of the main function
Defer has the potential to change the return value of the function, which is the most error-prone area.
The keyword _return_ is not an atomic operation, in fact _return_ is only a proxy for the assembly instruction _ret_, which is the jump program execution. For example, the return I statement actually takes two steps. The value of I is stored on the stack as the return value, and then the jump is performed. The execution time of defer is just before the jump, so there is still a chance that the return value will be manipulated during the deferred execution. The execution of return I is as follows:
Result = I Executiondefer
return
Copy the code
So based on this rule, for deferFuncReturn1,
func deferFuncReturn1(a) (ret int) {
ret = 10
defer func(a) {
ret++
fmt.Println("-- -- -- -- --", ret)
}()
return 2
}
Copy the code
The execution process is:
ret = 2
ret++
fmt.Println("-- -- -- -- --", ret)
return
Copy the code
So the final value of RET is 3.
In the case of deferFuncReturn2, since defer declared with parameters directly, we used snapshots and did not affect the return value of RET.
Rule 3 Deferred function execution is executed in last-in, first-out order, meaning that defer comes first and executes last
This rule is familiar, and defer executes in stack order.
Pit instance
Give an example of the mistake of using defer. One of the recommended ways to use transactions in go is to put Rollback into defer and determine if you want to Rollback by checking for an error or panic from the function.
func Update(a) (resp *baseinfo.Resp, err error) {
// Start the transaction
panicked := true
tx, err := db.TXBegin()
iferr ! =nil {
return resp, nil
}
defer func(a) {
ifpanicked || err ! =nil {
tx.Rollback()
}
}()
/ / update
err = h.update(shopId, tx)
iferr ! =nil {// Return on failure
return resp, nil
}
panicked = false
err = tx.Commit().Error
iferr ! =nil { // Return on failure
return resp, nil
}
return
}
Copy the code
The err that determines the Rollback is the named return value of the function. In the case of an error, the return value is assigned nil, which means that Rollback will not be performed if there is a failure.
The reason why err is not returned directly and nil is used is because of a framework design problem. Business errors are returned through RESP, and if err is returned directly, the framework will think it is an RPC error.
conclusion
To each knowledge point, need to have accurate understanding, especially in this opportunity to understand, otherwise easy to write a problem.
data
-
The rules for using defer in Golang
-
Go expert programming
The last
If you like my article, you can follow my public account (Programmer Malatang)
My personal blog is shidawuhen.github. IO /
Review of previous articles:
-
Design patterns
-
recruitment
-
thinking
-
storage
-
The algorithm series
-
Reading notes
-
Small tools
-
architecture
-
network
-
The Go