The deferred call's arguments are evaluated immediately

一世执手 提交于 2019-12-20 01:06:04

问题


In A Tour of Go is written:

The deferred call's arguments are evaluated immediately, but the function call is not executed until the surrounding function returns.

I have difficulty in understanding the first part of the quote. What is called immediately?

func def(s string) func() {
    fmt.Println("tier up")
    fmt.Println(s)
    return func(){ fmt.Println("clean up") }
}

func main() {
    defer def("defered line")()
    fmt.Println("main")
}

//Output:
//tier up
//defered line
//main
//clean up

https://play.golang.org/p/Av3mAEXxA4R

What is deferred here and what is evaluated immediately?


回答1:


To learn how defer and evaluations work, first let's look at the Spec: 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.

Both the function value (whose call is deferred) and its parameters are evaluated. But the deferred function is not yet called.

Let's iterate toward your example with small steps:

defer f("a")

In this case the function value is evaluated (which will be f), and the parameters are evaluated, which is a constant, so that's gonna be "a".

Next step:

defer f(g("a"))

In this case the function value is evaluated (which will be f), and the parameters are evaluated, which means g will be called with "a" (because g's return value is the param to f).

Next step:

defer f()()

This is valid if the f function returns a function. The function value will be evaluated (which means f will be called!) but its return value will not be called, that is what will be deferred.

defer f(g())()

In this case the deferred function is the return value of f, so to evaluate the deferred function value, f must be called, and to do that g must be called prior. The return value of f will be deferred (not called).

Back to your example:

defer def("defered line")()

The function value is evaluated, which is the return value of def, so def is called. The return value of def will be deferred. Its parameters are evaluated, but actually it has no parameters.

So logically this is what happens:

  1. The deferred function value is evaluated, which requires that:
    • def function must be called, which requires that:
      • Params to def are evaluated, it's a constant: "defered line"
  2. Parameters to the deferred function are evaluated; since there are no params to it, this step is done.

This is what happens sequentially if we lay out the above structure:

  • Param to def is evaluated: it's a constant "defered line"
  • def is called which prints tier up and its argument: defered line
  • The return value of def is not called, that is what's deferred.
  • Function main prints: main
  • Function main returns, so deferred functions are called now. The deferred function prints clean up



回答2:


The deferred call's arguments are evaluated immediately, but the function call is not executed until the surrounding function returns.

Above sentence means the deferred function arguments are evaluated at the line where they are deferred but the function will run after the surrounding function which is main returns.

A defer statement pushes a function call onto a list. The list of saved calls is executed after the surrounding function returns. Defer is commonly used to simplify functions that perform various clean-up actions.

Deferred function calls are executed in Last In First Out order after the surrounding function returns.

Deferred functions may read and assign to the returning function's named return values.

The above line clearly states that it will return the value to the main function.

For eg:-

func c() (i int) {
    defer func() { i++ }()
    return 1
}

Above function will return 2 value rather than 1. That's why this line

return func(){ fmt.Println("clean up") }

will be called in the last.

For more information on defer. Please read golang blog for defer




回答3:


It will be more clear if you change the argument you're passing to def(string) from a string literal (evaluated at compile time) to something more interesting, say:

func bar(s sting) string {
    return s + " bar "
}

func main() {
    defer def(bar(os.Args[1]) + "defered line")()
    fmt.Println("main")
}

When the defer def(bar(os.Args[1]) + "defered line")() statement is executed, the argument to def will be fully evaluated, and this means calling bar passing it the first command-line argument supplied by the user when running your program, taking whatever bar returned and appending a string literal to it.

The resulting string is then saved, and will be passed to def when it will run.




回答4:


In

defer def("defered line")()

def("defered line") and () are deferred call's arguments evaluated immediately.

def("defered line") evaluates to func(){ fmt.Println("clean up") } with side effects.



来源:https://stackoverflow.com/questions/51360229/the-deferred-calls-arguments-are-evaluated-immediately

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!