Defer usage clarification

前端 未结 2 1957
独厮守ぢ
独厮守ぢ 2020-12-07 04:48

Let\'s assume I have the following function

func printNumbers(){
 var x int

 defer fmt.Println(x)

 for i := 0; i < 5; i++{
  x++
 }
}

相关标签:
2条回答
  • 2020-12-07 05:01

    If the defer has arguments they are evaluated at the line of the defer-statement; this is illustrated in the following snippet, where the defer will print 0:

    func printNumber() {
       i := 0
       defer fmt.Println(i) // will print 0
       i++
       return
    }
    

    You can use an anonymous function as a defer statement if you want to postpone the execution of a statement or a function until the end of the enclosing (calling) function. Here is an updated example:

    func printNumbers() {
        x := 0
        defer func() { fmt.Println(x) }()
        for i:=0; i < 5; i++ {
            x++;
        }
        return
    }
    

    http://play.golang.org/p/YQGQ_8a0_9

    0 讨论(0)
  • 2020-12-07 05:14

    Generally what is important is x cannot be a parameter of the function you are deferring, because they are evaluated when defer is executed.

    1) With Anonymous Function

    Here's a solution using an anonymous function:

    defer func() { fmt.Println(x) }()
    

    Here x is not a parameter of the deferred anonymous function, so it will not be evaluated. Only when the anonymous function is executed and it calls fmt.Println().

    2) With Pointer

    Using a pointer (like &x) pointing to x would work, because only the address is evaluated, and the pointed value at the end will be 5 of course. The problem with this is that fmt.Println() will not print the pointed value but the pointer itself.

    But to demonstrate its working, see this helper function:

    func Print(i *int) {
        fmt.Println(*i)
    }
    

    And using it:

    defer Print(&x) // Will print 5 at the end
    

    3) With custom type

    This is similar to the pointer solution, but doesn't need a helper function. But it does need you to write your String() method:

    type MyInt int
    
    func (m *MyInt) String() string {
        return strconv.Itoa(int(*m))
    }
    

    And using it:

    var x MyInt
    
    defer fmt.Println(&x)
    
    for i := 0; i < 5; i++ {
        x++
    }
    

    When defer statement is executed, only the pointer will be evaluated (address of x, type of *Myint). And since the type *MyInt implements fmt.Stringer, fmt.Println() will call its String() method.

    4) Wrapping

    This is also similar to the pointer solution, and this will not even print exactly just 5 as you expect, but:

    Problem with #2 was that fmt.Println() will print the pointer and not the pointed value (which we solved with our own Print() function). However there are other types which are similar to pointers and fmt.Println() will print their content.

    So let's wrap the variable into a slice, and see what happens:

    x := []int{0}
    
    defer fmt.Println(x)
    
    for i := 0; i < 5; i++ {
        x[0]++
    }
    

    Prints:

    [5]
    

    Reason for seeing 5 is that a slice is a descriptor. When defer is evaluated, a copy is made of the slice (that will be passed to fmt.Println() when it gets executed) but it refers to the same underlying array.

    Also note that fmt.Println() prints the pointed content if the pointer is a pointer to a struct, array, slice, maps, so the following code also works:

    x := struct{ i int }{}
    
    defer fmt.Println(&x)
    
    for i := 0; i < 5; i++ {
        x.i++
    }
    

    And prints:

    &{5}
    
    0 讨论(0)
提交回复
热议问题