defer

Go in the function of the language returnstatement is not an atomic operation at the bottom, which is divided and assigned to the return instruction RET two steps. And deferbefore the time statement execution after returning value assignment, RET instruction is executed. Specifically as shown below:

 

 

 

When defer defined function, the external reference is variable in two ways, namely as a function of parameters and closures as reference. As a function of the parameter, the value is passed when put to defer defer defined, and cache them; closure as quoted, it determines the current value when the function actually invoked according to defer the whole context.

 

 

Consider the following example a:

func f5() (x int) {
    aa := func(){

    fmt.Println(x, "b")
    }

    defer fmt.Println(x, "a")
    defer aa()
    defer fmt.Println(x,"c")
    return 5
}
 
func main(){
 
fmt.Println(f5())
}
 
 
Results of the:
 

The c 0
5 . B
0 of a
5

 
Explanation:
 
fmt.Println (x, "a") function call an ordinary, at the bottom of exactly what to do
The examples are not considered, if the code is as follows:
x:=4
fmt.Println(x, "a")
 
When the program execution to fmt.Println (x, "a"), first open up a space in memory, spatial function can be called a run, this run space and storing local variables, and some other things.
Note, x is not a function of the outer body of the variables x, x here is the local variables in the body. Just happens to equal x external variables only.
Well open up space, and then assigned to the local variable x 4. This is the preparatory work before the operation. . . . . .
 
In this case, the preparatory work done directly run this function. . . If the defer preceded by one, i.e. defer fmt.Println (x, "a"), then, when the program runs to
defer fmt.Println (x, "a") when, defer this function call will fmt.Println done the preparatory work, and not let him run, only to wait until the end of the program to run.
 
And defer aa () phrase, preparation aa function just opens up a space to run, because this function has no parameters. Wait until aa real function last executed, to perform
fmt.Println (x, "b") phrase, or still open running space, mass participation that is assigned to the local variable x, and finally run the function. Note that when the parameter passing, x has become 5, therefore, to
fmt.Println (x, "b") when the local variables assigned a function of x, also assigned to 5.
 
 
 
 
Above this concept it is very important. In fact, to understand the above, it is not difficult to understand the closure
 
 
 
 
 
 
 
 
 
 
 
 
 
 

What is defer?

deferGo language is a mechanism provided for registration delay call: Let the function or statement (including the normal end or by return panic caused by abnormal end) after the current function completes execution.

deferStatements are intended to be operated in pairs in some scenarios: connecting opening / closing the connection; lock / release latch; file open / close files.

deferVery useful in some scenarios require recovery of resources, it is easy to do some cleanup operations before the end of the function. Open the resource statement in the next line, you can defer a direct shut resources before the function returns, can be quite elegant.

f, _ := os.Open("defer.txt")defer f.Close()

Note: The above code ignores err, in fact, should first determine whether the error, if wrong, then direct return then judge. fIs empty, if fempty, you can not call the function, and will direct the panic.f.Close()

Why defer?

Programmers when programming, often need to open some resources, such as database connections, files, locks, etc., these resources need to be released off after the run, otherwise it will cause a memory leak.

But programmers are people, it is people will make mistakes. Therefore, programmers often forget to turn off these resources. Golang provided directly in the language level deferkeyword in the open resource statement of the next line, you can directly use deferthe statement to register a shutdown resource operations after the end of the function. Because such a "small" syntactic sugar, the programmer forgot to write turned off resource statement is greatly reduced.

How rational use defer?

defer the use is very simple:

  1. f,err := os.Open(filename)

  2. if err != nil {

  3.    panic(err)

  4. }

  5.  

  6. if f != nil {

  7.    defer f.Close()

  8. }

In the vicinity of the statement opens the file, close the file defer statement. Thus, before the end of the function, automatically performs back defer statement to close the file.

Of course, defer there will be a small delay, especially special procedures particularly high requirements on time, avoid using it, ignore other general delay it brings.

defer Advanced

What is the underlying principle defer?

We look at the official deferexplanation:

Each time a “defer” statement executes, the function value and parameters to the call are evaluated as usual and saved anew but the actual function is not invoked. Instead, deferred functions are invoked immediately before the surrounding function returns, in the reverse order they were deferred. If a deferred function value evaluates to nil, execution panics when the function is invoked, not when the “defer” statement is executed.

Translate: defer statement executed every time, the function will "push", function parameters are copied down; when the outer function (non-code block, such as a for loop) exit, defer reverse the function performed in accordance with defined ; If the function performed defer to nil, would generate panic in the final call to the function.

defer statement does not execute immediately, but will enter a stack before the function return, it will be executed sequentially, last-out. That is to say also the first to be defined defer statement last execution. The reason, last-out function is defined later may depend on the resources front, the natural first execution; otherwise, if the previous execution first, then rely on the back, there is no function.

When defer defined function, the external reference is variable in two ways, namely as a function of parameters and closures as reference. As a function of the parameter, the value is passed when put to defer defer defined, and cache them; closure as quoted, it determines the current value when the function actually invoked according to defer the whole context.

Behind defer statement in the course of implementation, the parameters of the function call will be saved, which is a copy. Real time execution actually used is a copy of the variable, so if this variable is a "value", and then when the definition is the same. If this variable is a "reference", then it may be time-defined and inconsistent.

for example:

  1. func main() {

  2.    var whatever [3]struct{}

  3.  

  4.    for i := range whatever {

  5.        defer func() {

  6.            fmt.Println(i)

  7.        }()

  8.    }

  9. }

Results of the:

222

Defer behind with a closure (will be mentioned later), i is the "reference" type variable, the final value of i is 2, so the last three printed 2.

With the basis of the above, let's examine what the results:

  1. type number int

  2.  

  3. func (n number) print()   { fmt.Println(n) }

  4. func (n *number) pprint() { fmt.Println(*n) }

  5.  

  6. func main() {

  7.    var n number

  8.  

  9.    defer n.print()

  10.    defer n.pprint()

  11.    defer func() { n.print() }()

  12.    defer func() { n.pprint() }()

  13.  

  14.    n = 3

  15. }

The results are:

3330

The fourth statement is defer closure, references n external function, the final result is 3; and the third with the fourth defer statement; second defer statement, n is a reference, a final evaluation was 3. The first defer statement, the value of n determined directly, at the beginning of n = 0, so in the end is 0;

Use defer principle

In some cases, we deliberately used to defer evaluated, and then delayed nature of the call. Imagine this scenario: in a function, need to open two files are merged operations, the merger is complete, close open file handles in function is completed.

  1. func mergeFile() error {

  2.    f, _ := os.Open("file1.txt")

  3.    if f != nil {

  4.        defer func(f io.Closer) {

  5.            if err := f.Close(); err != nil {

  6.                fmt.Printf("defer close file1.txt err %v\n", err)

  7.            }

  8.        }(f)

  9.    }

  10.  

  11.    // ……

  12.  

  13.    f, _ = os.Open("file2.txt")

  14.    if f != nil {

  15.        defer func(f io.Closer) {

  16.            if err := f.Close(); err != nil {

  17.                fmt.Printf("defer close file2.txt err %v\n", err)

  18.            }

  19.        }(f)

  20.    }

  21.  

  22.    return nil

  23. }

The above code will use the principle of defer, defer function definition when the parameters have been copied into it, then, the real implementation of close () function when it is closed just right "file", and was well! Imagine if this will not function f as a parameter passed into the case, the last two statements the same file is closed, and the last one to open the file.

But when calling close () function, we should note that: first determine whether to call the body is empty, otherwise it will panic such as the above code fragment, the first judgment fis not empty, will call the function, so the safest.Close()

Dismantling defer command

If you defer as above, a brief introduction to (in fact, not a simple matter), this world is perfect. Things did not always that simple, defer used properly, many will jump into the pit.

The key to understanding these pits is this statement:

return xxx

Above this statement after compiled into a three instructions:

1. Return Value = xxx2. 3. The function call empty return defer

Step 3 Return statement is true command, step 2 is defined defer statements, there may be operating return value.

Let's look at two examples, try to return to the dismantling of the statement and defer sentence in the correct order.

The first example:

func f() (r int) {     t := 5     defer func() {       t = t + 5     }()     return t}

After dismantling:

  1. func f() (r int) {

  2.     t := 5

  3.  

  4.     // 1. 赋值指令

  5.     r = t

  6.  

  7.     // 2. defer被插入到赋值与返回之间执行,这个例子中返回值r没被修改过

  8.     func() {        

  9.         t = t + 5

  10.     }

  11.  

  12.     // 3. 空的return指令

  13.     return

  14. }

The second step there is no operation returns the value r, and therefore, main calling function f () to give 5.

A second example:

func f() (r int) {    defer func(r int) {          r = r + 5    }(r)    return 1}

After dismantling:

  1. func f() (r int) {

  2.     // 1. 赋值

  3.     r = 1

  4.  

  5.     // 2. 这里改的r是之前传值传进去的r,不会改变要返回的那个r值

  6.     func(r int) {

  7.          r = r + 5

  8.     }(r)

  9.  

  10.     // 3. 空的return

  11.     return

  12. }

Thus, main calling function f () to give 1.

Parameters defer statement

Value defer statement expression in the definition have been identified. The following shows three functions:

  1. func f1() {

  2.    var err error

  3.  

  4.    defer fmt.Println(err)

  5.  

  6.    err = errors.New("defer error")

  7.    return

  8. }

  9.  

  10. func f2() {

  11.    var err error

  12.  

  13.    defer func() {

  14.        fmt.Println(err)

  15.    }()

  16.  

  17.    err = errors.New("defer error")

  18.    return

  19. }

  20.  

  21. func f3() {

  22.    var err error

  23.  

  24.    defer func(err error) {

  25.        fmt.Println(err)

  26.    }(err)

  27.  

  28.    err = errors.New("defer error")

  29.    return

  30. }

  31.  

  32. func main() {

  33.    f1()

  34.    f2()

  35.    f3()

  36. }

operation result:

<nil>defer error<nil>

1st and 3rd function as an argument because the definition of when it will be evaluated, when values are defined nil err variable, so when the final print are nil. Parameters of the two functions will actually when evaluated as defined, but, in the second example is a closure, it references the variable err during execution eventually became defer errora. Of the closure, there is described later herein.

The third error function is still relatively easy to commit, in a production environment, it is easy to write such an error code. Finally defer statement did not play a role.

What Closures are?

Closure is a function of its physical environment REFERENCE TO RELATED combination, namely:

= + Closure function reference environment

General functions have function names, but there is no anonymous function. Anonymous functions can not exist independently, but can be called directly or assignment to a variable. Anonymous function is also known as a closure, a closure inherited the scope function declarations when. In Golang, all anonymous functions are closures.

There is an inappropriate example, the closure can be seen as a class, a closure function call is an example of a class. Closures at runtime can have multiple instances, it will capture down in the same scope of variables and constants, regardless of when the closure is in what is called (instantiated), you can use these variables and constants. Further, the closure capture variables and constants are passed by reference, not by value.

Here is a simple example:

  1. func main() {

  2.    var a = Accumulator()

  3.  

  4.    fmt.Printf("%d\n", a(1))

  5.    fmt.Printf("%d\n", a(10))

  6.    fmt.Printf("%d\n", a(100))

  7.  

  8.    fmt.Println("------------------------")

  9.    var b = Accumulator()

  10.  

  11.    fmt.Printf("%d\n", b(1))

  12.    fmt.Printf("%d\n", b(10))

  13.    fmt.Printf("%d\n", b(100))

  14.  

  15.  

  16. }

  17.  

  18. func Accumulator() func(int) int {

  19.    var x int

  20.  

  21.    return func(delta int) int {

  22.        fmt.Printf("(%+v, %+v) - ", &x, x)

  23.        x += delta

  24.        return x

  25.    }

  26. }

Results of the:

(0xc420014070, 0) - 1(0xc420014070, 1) - 11(0xc420014070, 11) - 111------------------------(0xc4200140b8, 0) - 1(0xc4200140b8, 1) - 11(0xc4200140b8, 11) - 111

Closure references variables x, a, b can be seen as two different instances, instances affecting each other. Examples of internal, x address variable is the same, thus having "additive effect."

defer cooperate recover

Golang been criticized more is its error, often an error everywhere. Programming time is always returns an error, left to deal with the caller. If that is a fatal error, such as when the program is executed to initialize the problem, directly off the panic, to save on the line after running a bigger problem.

Sometimes, however, we need to recover from the exception. For example, the server has encountered serious problems, resulting in a panic, then we can do some "mopping up" at least until the program crashes, such as the client closes the connection to prevent the client waits, and so on.

panic currently executing program will be stopped, not just the current coroutine. Prior to this, it will be done orderly execution of the current coroutine defer the list of statements, other coroutine hung the defer statement is not guaranteed. Therefore, we often hang in the defer recover in a statement, to prevent program hang directly, which played an effect.try...catch

Note, recover () function is effective only in the context of defer (and only valid through anonymous function calls defer using), direct call, it will only return nil.

  1. func main() {

  2.    defer fmt.Println("defer main")

  3.    var user = os.Getenv("USER_")

  4.  

  5.    go func() {

  6.        defer func() {

  7.            fmt.Println("defer caller")

  8.            if err := recover(); err != nil {

  9.                fmt.Println("recover success. err: ", err)

  10.            }

  11.        }()

  12.  

  13.        func() {

  14.            defer func() {

  15.                fmt.Println("defer here")

  16.            }()

  17.  

  18.            if user == "" {

  19.                panic("should set user env.")

  20.            }

  21.  

  22.            // 此处不会执行

  23.            fmt.Println("after panic")

  24.        }()

  25.    }()

  26.  

  27.    time.Sleep(100)

  28.    fmt.Println("end of main function")

  29. }

The above panic will eventually be captured to recover. This approach is often used in the main flow of a http server. A chance request may trigger a bug, then use recover capture panic, stabilize the main flow, does not affect other requests.

Programmers learn by monitoring the occurrence of panic, according to the appropriate point in time positioning location of the log to find the cause of the occurrence of panic, neat and quick, on-line repair. A look around, we are hard at doing their own thing, just perfect: secretly fix a bug, I found no! Hey!

postscript

defer very easy to use, there would be no problem under normal circumstances. But only in-depth understanding of the principles will avoid defer its tender trap. Armed with its principles, it will write the code easy to understand and maintain.

 

 

 

 

 

 

 

 

 

 

 

 

Look at two questions: Reprinted from https://www.liwenzhou.com/posts/Go/09_function/

func f1() int { x := 5 defer func() { x++ }() return x } func f2() (x int) { defer func() { x++ }() return 5 } func f3() (y int) { x := 5 defer func() { x++ }() return x } func f4() (x int) { defer func(x int) { x++ }(x) return 5 } func main() { fmt.Println(f1()) fmt.Println(f2()) fmt.Println(f3()) fmt.Println(f4()) }







func calc(index string, a, b int) int { ret := a + b fmt.Println(index, a, b, ret) return ret } func main() { x := 1 y := 2 defer calc("AA", x, calc("A", x, y)) x = 10 defer calc("BB", x, calc("B", x, y)) y = 20 }
 

 

 

Guess you like

Origin www.cnblogs.com/saolv/p/11875130.html