So, let’s see how it works behind the scenes
If we look at the internals of the return statement in the compiler (in the ssa builder of the Go compiler), we can see that the compiler puts “defer calls” “just before the return” and then does “return”. Please check out the underlined words in this paragraph to see what I mean.
The runtime reserves a stack space in the surrounding func’s stack for defers — for example, this also enables using the named result params to be changed by the deferred func. So, they share the same stack.
In the exit func you can even see that its behavior has been commented as “before” — not “after”. Exit func is used in ssa builder of Go compiler backend to generate a code for a func’s return.
“exit processes any code that needs to be generated just before returning” — here.
So, basically, defers run like this:
return /* defers run here */ ...
But, technically run like this:
/* deferred funcs run here */
You may say that, it can’t be. Because, in this example of my reader, he sees that the value of “i” variable is 0 not 1. But, behind the scenes, the compiler actually puts the variable into a temporary variable to make you see as if it was 0, actually it becomes 1.
👉 Check out the old-ssa code (~the interim compiled code) that I generated for this example, here.
So, how it works?
Not like this:
// not here! - not after.
Or not like this:
// not here - not after
But, as you see within the example code, it “acts like it runs defers after the func returns”.
In this article I tried to write about the internal behavior, not about according to the Go Spec.