I'm reading The Go Programming Language Specifications
and found myself not truly understand with "()" after closure body:
In Function literals
:
func(ch chan int) { ch <- ACK }(replyChan)`
In Defer statements
's example:
// f returns 1
func f() (result int) {
defer func() {
result++
}() // why and how?
return 0
}
I'm not clear about the reason to add & usage of "()" after closure body, hope someone can explain this clearly.
It's not that ()
must be added after (only) a closure in defer
. The language specs for the defer statement mandate that its 'Expression' always must be a function call.
And why is it so? It's the same as with any other function, in 'defer' or not:
Consider:
func f() int { return 42 }
and
a := f
vs
b := f()
The first expression RHS is a function value. In the second version the RHS is the value returned by the function - i.e. a function call.
So is the semantics of:
defer f
vs
defer f()
except that the first version doesn't make sense in the context of 'defer', and so the specifications mention that it must be the second form (only).
It's IMHO also easier to learn because of the orthogonality with the above discussed function call outside of the 'defer' statement.
Also note that a function call is not only fn-expr followed by ()
, but an expression list is generally inside the parenthesis (including an empty list). There's a big difference between:
for i := range whatever {
defer func() { fmt. Println(i) }()
}
and
for i := range whatever {
defer func(n int) { fmt. Println(n) }(i)
}
The first version prints the value of 'i' in the moment when the closure executes, the second prints the value of 'i' in the moment when the defer statement was executed.
References
The Go Programming Language Specification
Function types
A function type denotes the set of all functions with the same
parameter and result types.
FunctionType = "func" Signature .
Signature = Parameters [ Result ] .
Result = Parameters | Type .
Parameters = "(" [ ParameterList [ "," ] ] ")" .
ParameterList = ParameterDecl { "," ParameterDecl } .
ParameterDecl = [ IdentifierList ] [ "..." ] Type .
Function declarations
A function declaration binds an identifier, the function name, to a
function.
FunctionDecl = "func" FunctionName Signature [ Body ] .
FunctionName = identifier .
Body = Block .
Function literals
A function literal represents an anonymous function. It consists of a
specification of the function type and a function body.
FunctionLit = FunctionType Body .
Function literals are closures: they may refer to variables defined in
a surrounding function. Those variables are then shared between the
surrounding function and the function literal, and they survive as
long as they are accessible.
A function literal can be assigned to a variable or invoked directly.
Calls
Given an expression f
of function type F
,
f(a1, a2, … an)
calls f
with arguments a1, a2, … an
.
In a function call, the function value and arguments are evaluated in
the usual order. After they are evaluated, the parameters of the call
are passed by value to the function and the called function begins
execution. The return parameters of the function are passed by value
back to the calling function when the function returns.
Defer statements
A "defer
" statement invokes a function whose execution is deferred
to the moment the surrounding function returns.
DeferStmt = "defer" Expression .
The expression must be a function or method call. Each time the
"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. Instead, deferred calls are executed in LIFO order
immediately before the surrounding function returns, after the return
values, if any, have been evaluated, but before they are returned to
the caller.
Since you are still confused, here's another attempt to provide an answer to your question.
In the context of your question, ()
is the function invocation operator.
For example, the function literal
func(i int) int { return 42 * i }
represents an anonymous function.
The function literal followed by the ()
function invocation operator
func(i int) int { return 42 * i }(7)
represents an anonymous function which is then invoked directly.
Ordinarily, in a function call, the function value and arguments are evaluated in the usual order. After they are evaluated, the parameters of the call are passed by value to the function and the called function begins execution. The return parameters of the function are passed by value back to the calling function when the function returns.
However, invoking the function through the defer statement is a special case. Each time the "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. Instead, deferred calls are executed in LIFO order immediately before the surrounding function returns, after the return values, if any, have been evaluated, but before they are returned to the caller.
The defer statement expression must be a function or method call that is invoked directly, not just a function or method literal which is not invoked directly. Therefore, the function or method literal needs to be followed by the ()
function invocation operator so that the defer statement expression is a function or method call.
The defer statement
defer func(i int) int { return 42 * i }(7)
is valid.
The defer statement
defer func(i int) int { return 42 * i }
is invalid: syntax error: argument to go/defer must be function call
.