I have a question about golang defer: Is golang defer statement execute before or after return statement?
I have read Defer_statements. But I do not got the answer.
I made a simple test:
func test1() (x int) {
defer fmt.Printf("in defer: x = %d
", x)
x = 7
return 9
}
func test2() (x int) {
defer func() {
fmt.Printf("in defer: x = %d
", x)
}()
x = 7
return 9
}
func test3() (x int) {
x = 7
defer fmt.Printf("in defer: x = %d
", x)
return 9
}
func main() {
fmt.Println("test1")
fmt.Printf("in main: x = %d
", test1())
fmt.Println("test2")
fmt.Printf("in main: x = %d
", test2())
fmt.Println("test3")
fmt.Printf("in main: x = %d
", test3())
}
In test1()
, using Printf
to print x after defer. In test2()
, using a anonymous function to print x after defer. In test3()
, using Printf
to print x after defer, but defer after x = 7
.
But the result is:
test1
in defer: x = 0
in main: x = 9
test2
in defer: x = 9
in main: x = 9
test3
in defer: x = 7
in main: x = 9
So, is any one can explain: 1. why got this result? why test1 prints 0, test2 print9, test3 prints 7. 2. is defer statement excutes after return or before return?
Thanks a lot.
Thanks @dev.bmax @Tranvu Xuannhat @rb16. With your help, I found the key explanation from Defer_statements.
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.
We can break up defer ...
into three parts.
I made a new test4 to explain.
func test4() (x int) {
defer func(n int) {
fmt.Printf("in defer x as parameter: x = %d
", n)
fmt.Printf("in defer x after return: x = %d
", x)
}(x)
x = 7
return 9
}
In test4,
func(n int)(0)
onto stack.func(n int)(0)
after return 9
, n in fmt.Printf("in defer x as parameter: x = %d ", n)
has been evaluated, x in fmt.Printf("in defer x after return: x = %d ", x)
will be evaluated now, x is 9。So, got the result:
test4
in defer x as parameter: x = 0
in defer x after return: x = 9
in main: x = 9
In the first test, the value of the parameter x
is evaluated when the line containing the defer
runs. And it happens in line 1 when x
is still 0.
Note, that even though fmt.Printf
will be called later, it's parameters are evaluated ahead of time.
In the third test, the value of the parameter x
is set to 7 before the defer
statement runs. Again, this happens before the actual invocation of fmt.Printf
.
The second test is a little different. Variable x
is within the scope of the anonymous function. It's value is evaluated when the function runs, which happens when the test function returns. By that time x
is 9.
I will refer to Defer_statements to explain your results.
Question 2: Defer executes after return.
Question 1: From the docs
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.
Test 1: Defer invokes fmt.Println. fmt.Println evaluates the value of x, which is 0 (zero value). test1() return 9 in the main function.
Test 2: Defer invokes func() {}. fmt.Println only evaluates x when func() {} is executed (after return). Hence, x is evaluated to 9. test2() return 9 in the main function.
Test 3: Defer invokes fmt.Println. fmt.Println evaluates the value of x, which is 7 (x is assigned to 7). test3() return 9 in the main function.
It's not before or after, it's all about if the defer statement is present in the stack or not. if it is (if the control reaches to the defer statement it saves stores statement into the stack), then after it will execute after the return statement.
for example -
func testing() err {
x, err := doSomething();
if err != nil {
return;
}
defer fmt.Println("hello")
return nil
}
So if err != nil
this defer will never execute, because program returned before the control reaches to the defer statement.
Now come to your Question (all will reach to the defer statement) 1. case, the statement stored in the stack is defer fmt.Printf("in defer: x = %d ", x)
(with zero value of x)
defer func() { fmt.Printf("in defer: x = %d ", x) }()
Which is dependent on x (Note in the stack the defer statement is a function, and the value of a function will be assigned at the time of function execution)
defer fmt.Printf("in defer: x = %d ", x)
with value 7