Reputation: 2014
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\n", x)
x = 7
return 9
}
func test2() (x int) {
defer func() {
fmt.Printf("in defer: x = %d\n", x)
}()
x = 7
return 9
}
func test3() (x int) {
x = 7
defer fmt.Printf("in defer: x = %d\n", x)
return 9
}
func main() {
fmt.Println("test1")
fmt.Printf("in main: x = %d\n", test1())
fmt.Println("test2")
fmt.Printf("in main: x = %d\n", test2())
fmt.Println("test3")
fmt.Printf("in main: x = %d\n", 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.
Upvotes: 29
Views: 21682
Reputation: 21
let me answer the second question first:
The Go defer
statement executes after the return
statement sets the return values but before the function actually returns to its caller.
So now for test1
and test2
, it’s the same idea, and based on the official Go documentation:
"the function value and parameters to the call are evaluated as usual".
Naturally, we trace it line by line. For the test1
function, x
has the zero value for int, so it will be 0
, and for test3
, the value of x
at the moment of execution of the defer statement is equal to 7
.
func test1() (x int) {
defer fmt.Printf("in defer: x = %d\n", x)
x = 7
return 9
}
func test3() (x int) {
x = 7
defer fmt.Printf("in defer: x = %d\n", x)
return 9
}
But test2
is more tricky, and I think to explain what it is doing, we need to have a background of Closures:
"A closure can capture and access variables from its surrounding function's scope."
This explains the behavior of the anonymous function in test2
. The deferred closure captures a reference to x
, not its value (it can track the modification of x
), so it observes the final value of x
after being set by the return
statement.
func test2() (x int) {
defer func() {
fmt.Printf("in defer: x = %d\n", x)
}()
x = 7
return 9
}
And not surprisingly for all of them, return 9
sets x
(is a named return value) to 9
.
Upvotes: 0
Reputation: 1
defer
is executed after the result parameter is set, but before the outerFunction exit.
func deferLearning() {
defer printStack()
defer panicTest()
fmt.Printf("result is 8: %d\n", changeNamedResultParameter())
fmt.Printf("result is 7: %d\n", changeLocalVariable())
}
func printStack() {
if e := recover(); e != nil {
fmt.Println(string(debug.Stack()))
}
}
func panicTest() {
panic("panic in defer")
}
func changeNamedResultParameter() (x int) {
defer func(n int) {
fmt.Printf("defer parameter 0, %d\n", n)
fmt.Printf("after the named result parameter is set, but before outerFunction exit: 7, %d\n", x)
x++ //modify the named result parameter
}(x)
x = 7
return x
}
func changeLocalVariable() int {
var x int
defer func(n int) {
fmt.Printf("defer parameter 0, %d\n", n)
fmt.Printf("get local variable x, after unnamed result parameter is set, but before outerFunction exit: 7, %d\n", x)
x++ //modify the local variable
}(x)
x = 7
return x
}
You will see the stack trace of the calling goroutine:
runtime/debug.Stack()
C:/Program Files/Go/src/runtime/debug/stack.go:24 +0x65
demo/learning.printStack()
D:/go/demo/learning/learn_defer.go:17 +0x2a
panic({0x5f4d20, 0x669a90})
C:/Program Files/Go/src/runtime/panic.go:838 +0x207
demo/learning.panicTest()
D:/go/demo/learning/learn_defer.go:22 +0x27
demo/learning.deferLearning()
D:/go/demo/learning/learn_defer.go:13 +0xef
Upvotes: 0
Reputation: 2773
Whenever a program executes, it execute line by line.
When a program finds defer statement, it push that defer statement in the stack and move forward.
Once the program ends, it start executing those defer statement in the stack from top. The order of execution is Last In First Out.
Now, understand the basics. To execute the defer at the end of program, it must be a reachable code, it must execute to be saved in stack. If program returns before defer statement then it will not execute defer at the end of the program.
package main
import (
"fmt"
)
func main() {
defer fmt.Println("main's defer")
a()
b()
}
func a() {
defer fmt.Println("a: defer before return")
fmt.Println("a returns")
return
}
func b() {
defer fmt.Println("b: defer before return")
fmt.Println("b returns")
return
defer fmt.Println("b: defer after return")
}
Output
a returns
a: defer before return
b returns
b: defer before return
main's defer
"b: defer after return"
didn't print.
Upvotes: 4
Reputation: 21
As per the documentation of defer
at golang.org,
Go's defer statement schedules a function call (the deferred function) to be run immediately before the function executing the defer returns.
The arguments to the defer statement are evaluated first, then moved to the defer stack and then executed before the enclosing function returns.
Example 1:
Syntax 1:
func display() (x int) {
x = 10
defer func() {
fmt.Println(x)
}()
return 5
}
The above code can be re-written as,
Syntax 2:
func display() (x int) {
x = 10
defer func() {
fmt.Println(x)
}()
x = 5
// Deferred functions are executed.
return
}
Result:
5
The variables passed to the anonymous functions from the enclosing function are passed by reference. The deferred function when it is being executed, takes the values held in that memory location at the time of execution.
Upvotes: 2
Reputation: 1
package main
import "fmt"
func deque(a *[]int) {
*a = (*a)[1:]
}
func test_defer(a* []int) int{
defer deque(a)
return (*a)[0]
}
func main() {
a := []int{1,2}
fmt.Println(test_defer(&a))
fmt.Println(a)
}
try this code
go run test.go
1
[2]
test_defer return 1, so defer is executed after return
Upvotes: -1
Reputation: 2014
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", n)
fmt.Printf("in defer x after return: x = %d\n", 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", n)
has been evaluated, x in fmt.Printf("in defer x after return: x = %d\n", 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
Upvotes: 34
Reputation: 346
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\n", x)
(with zero value of x)
defer func() {
fmt.Printf("in defer: x = %d\n", 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\n", x)
with value 7 Upvotes: 5
Reputation: 604
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.
Upvotes: 5
Reputation: 10601
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.
Upvotes: 5