2

What happens to the defer in the code below?

package main

import "net/http"

func main() {
    resp, _ := http.Get("http://google.com")
    defer resp.Body.Close()
    resp, _ = http.Get("http://stackoverflow.com")
    defer resp.Body.Close()
}

There are two HTTP GET calls, both return to the same variable. Does defer stack the operation, leading to two Close() calls, or will only one be executed when main() finishes? (if the latter: will that be the first or second defer that gets executed?)

WoJ
  • 27,165
  • 48
  • 180
  • 345
  • The answer, as often, lies in the [language spec](https://golang.org/ref/spec#Defer_statements): "_Each time a "defer" statement executes, the function value and parameters to the call are evaluated as usual and saved anew_ [...]". In your example, two deferred calls will be executed; the value at the point of each deferred statement will be used. – jub0bs May 23 '21 at 12:27
  • The tricky part here is whether `resp` and then `resp.Body` is a `struct` that gets *overwritten* by the second `http.Get`, or whether it's a *pointer* and the second `http.Get` gets a new and different `resp` and `resp.Body`. Since `http.Get` returns a new pointer each time, this does the right thing. See the linked duplicate for more. – torek May 23 '21 at 23:03

1 Answers1

2

Yes, the defer does stack the reference to the variable resp leading to two Close calls. For example, in the following code, the cleanUp function is called with two different A Values:

package main

import "fmt"

type Resp struct {
    A int
}

func (r *Resp) cleanUp() {
    fmt.Println("Cleaned up ", r.A)
}

func main() {
    r := &Resp{1}   
    defer r.cleanUp()   
    
    fmt.Println("I am doing something here!")

    r = &Resp{2}
    defer r.cleanUp()
}

Above code outputs:

I am doing something here!
Cleaned up  2
Cleaned up  1

Thus even though the value of r is overwritten, it doesn't lead to overwriting of variable in the defer statement.

Sai Ravi Teja K
  • 569
  • 4
  • 11
  • I have no doubts that two **different** `defer` statements will both be executed. My problem is that both use the same `resp` (with different values) so if, say, a pointer was used then only one would have been executed. – WoJ May 23 '21 at 12:49
  • It doesn't matter if it is a pointer or not. If defer is used twice then the corresponding method/s will get executed twice. – Sai Ravi Teja K May 23 '21 at 12:54
  • I have updated the above answer. – Sai Ravi Teja K May 23 '21 at 12:57
  • @SaiRaviTejaK While it doesn’t matter to be executed twice, in this particular case it matter for correctness, it will close 2 time the second resp at the end. See: [on playground](https://play.golang.org/p/JFfyRZBGItz) – mgagnon May 23 '21 at 14:36
  • @WoJ For correctness, you need to use 2 different resp variables. See [on playground](https://play.golang.org/p/JFfyRZBGItz) if you use the same. – mgagnon May 23 '21 at 14:40
  • @mgagnon: thanks - so as I was suspecting, both calls (and now it does not matter that they are two of them) will handle only one (the last) call. This is what I was concerned with. Could you please turn your comment into an answer because it will be the correct one (= deferring twice will in fact only make one actual close) – WoJ May 23 '21 at 15:58
  • @mgagnon Then this is not at all related to the `defer` statement. You are invoking same method (on same instance) twice. And since `defer` executes only at the end of the function execution both `defer` statements will be invoking same `method` – Sai Ravi Teja K May 23 '21 at 16:11
  • @SaiRaviTejaK Ho, I think you are right: May be this example would describe better the OP's case: https://play.golang.org/p/YV3W23ZbT8j – mgagnon May 23 '21 at 16:23
  • @WoJ I was wrong, This example from [Go Playground](https://play.golang.org/p/YV3W23ZbT8j) is more representative. The second `resp` pointer assignment mimic your second http.Get(...). I think each `defer` keep a copy of each pointer respecteively. – mgagnon May 23 '21 at 16:28
  • @mgagnon Got it. I have updated the answer. Thank! – Sai Ravi Teja K May 23 '21 at 16:45