2

There is a passage in the "Mastering Concurrency in Go" book which made me think I might be missing something about "defer" functions.

You should also take note that any data passed by reference may be in an unexpected state.

func main() {
    aValue := new(int)
    defer fmt.Println(*aValue)

    for i := 0; i < 100; i++ {
         *aValue++
    }
}

This prints 0, I thought, because according to spec:

Each time a "defer" statement executes, the function value and parameters to the call are evaluated as usual and saved anew

That is, *aValue is 0 when defer is called and that's why at the end it prints 0. Whether or not a pointer is passed to the differ function in this case is irrelevant.

Is my understanding correct or am I missing something?

Amir Keibi
  • 1,991
  • 28
  • 45
  • 5
    I don't understand what it's talking about either. The phrase "passed by reference" in a Go book is strange too, since all values in Go are passed by value. – JimB Jan 04 '17 at 18:56
  • Well, the example is passing a pointer. I think that's what he means by passing by reference. Because although the pointer is copied, but the copy is still pointing to the same memory. – Amir Keibi Jan 04 '17 at 18:58
  • 4
    The example isn't passing a pointer anywhere, it's dereferencing the pointer and passing the int value (and "pass by reference" has a meaning separate from "passing a pointer value") – JimB Jan 04 '17 at 18:59
  • 2
    Possible duplicate of [Defer usage clarification](http://stackoverflow.com/questions/31404471/defer-usage-clarification/31404704#31404704); and [Golang defer clarification](http://stackoverflow.com/questions/28893586/golang-defer-clarification/28894103#28894103). – icza Jan 04 '17 at 20:44
  • @JimB Right you are! That was me not paying attention. But that's beside the point. I think the answer from AJcodez clarified this. – Amir Keibi Jan 04 '17 at 21:14
  • What is the negative vote for? Is this not a right question? – Amir Keibi Jan 05 '17 at 17:52
  • I have a feeling what the author meant is that, if you passed `aValue` rather than `*aValue`, since you're passing in a reference (ok, a pointer by value, but pedantry aside, you're effectively passing in a reference to the underlying integer), that integer's value may change before the deferred function is called, and thus have a different value (be in an unexpected state) than it did when you deferred the function. – Kaedys Jan 05 '17 at 22:32

2 Answers2

3

Consider a situation using structs.

type User struct {
    Name string
}

func main() {
    user := User{}
    defer fmt.Printf("%#v\n", user)
    user.Name = "AJ"
}

You know defer should run at the end, so you might expect to see User{Name: "AJ"} but instead you get User{Name: ""} because defer binds parameters.

If you use a pointer it works.

    user := &User{}

If you use a closure, it works.

    defer func() {
        fmt.Printf("%#v\n", user)
    }()
AJcodez
  • 31,780
  • 20
  • 84
  • 118
  • Although this is explaining what I already mentioned in the question (how parameters are evaluated), but the use of struct point out what the author might have been trying to say. – Amir Keibi Jan 04 '17 at 21:22
  • There are some other gotchas with defers. The reference to the called function is saved. All parameters are evaluated and saved (so if you make other function calls as parameters they will execute immediately not after execution). – Daniel Williams Jan 04 '17 at 22:22
2

The defer statement is "evaluating" the parameters and saving the result, and the result of evaluating *aValue is 0 at the time of the defer call. Something like this may be what you're looking for:

func main() {
    aValue := new(int)
    defer func() { fmt.Println(*aValue) }()

    for i := 0; i < 100; i++ {
        *aValue++
    }
}
dalton_c
  • 6,876
  • 1
  • 33
  • 42
  • I'm aware of that. Thanks. But this isn't the answer to my question. I'm wondering what the author is trying to point out. – Amir Keibi Jan 04 '17 at 21:08