James Shi
James Shi

Reputation: 2014

Is golang defer statement execute before or after return statement?

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 Printfto print x after defer. In test2(), using a anonymous function to print x after defer. In test3(), using Printfto 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

Answers (9)

Younesious
Younesious

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

frihed
frihed

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

Shubham Chadokar
Shubham Chadokar

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.

Playground

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

icemelting
icemelting

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

ist xu
ist xu

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

James Shi
James Shi

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.

  1. invoke defer, evaluating the value of function parameter.
  2. execute defer, pushing a function in to stack.
  3. execute functions in stack after return or panic.

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,

  1. invoke defer, evaluating the value of n, n = x = 0, so x as parameter is 0.
  2. execute defer, pushing func(n int)(0) onto stack.
  3. execute 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

rb16
rb16

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)

  1. case - the statement stored in the stack is A function

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)

  1. Case - defer fmt.Printf("in defer: x = %d\n", x) with value 7

Upvotes: 5

Tranvu Xuannhat
Tranvu Xuannhat

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

dev.bmax
dev.bmax

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

Related Questions