问题
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:
- 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"
- Params to
- 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 printstier 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 printsclean 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