为什么在Golang的封闭体后添加“()”?
我正在阅读The Go Programming Language Specifications
,发现自己在封闭体之后没有真正理解“()”:
在Function literals
:
func(ch chan int){ch < – ACK} (replyChan) `
在Defer statements
的例子中:
// f returns 1 func f() (result int) { defer func() { result++ }() // why and how? return 0 }
我不清楚在封闭体之后添加和使用“()”的原因,希望有人能够解释清楚。
这不是()
必须在(仅) 封闭后添加。 推迟语句的语言规范要求它的“expression式” 总是一个函数调用。
为什么呢? 这与其他function一样,在“推迟”或不推迟:
考虑:
func f() int { return 42 }
和
a := f
VS
b := f()
第一个expression式RHS是一个函数值。 在第二个版本中,RHS是函数返回的值,即函数调用。
所以是的语义:
defer f
VS
defer f()
除了第一个版本在“推迟”的情况下是没有意义的,因此规范提到它必须是第二种forms(仅)。
这是恕我直言,也更容易学习,因为与上述讨论函数调用之外的'延迟'陈述的正交性。
另请注意,函数调用不仅仅是fn-expr后跟()
,而且expression式列表通常在括号内(包括一个空列表)。 之间有一个很大的区别:
for i := range whatever { defer func() { fmt. Println(i) }() }
和
for i := range whatever { defer func(n int) { fmt. Println(n) }(i) }
第一个版本在执行闭包的同时打印“i”的值,第二个版本在执行延迟语句的瞬间打印“i”的值。
参考
Go编程语言规范
函数types
函数types表示具有相同参数和结果types的所有函数的集合。
FunctionType = "func" Signature . Signature = Parameters [ Result ] . Result = Parameters | Type . Parameters = "(" [ ParameterList [ "," ] ] ")" . ParameterList = ParameterDecl { "," ParameterDecl } . ParameterDecl = [ IdentifierList ] [ "..." ] Type .
函数声明
函数声明将标识符(函数名称)绑定到函数。
FunctionDecl = "func" FunctionName Signature [ Body ] . FunctionName = identifier . Body = Block .
函数文字
函数文字表示一个匿名函数。 它由函数types和函数体的规范组成。
FunctionLit = FunctionType Body .
函数文字是闭包:它们可能引用周围函数中定义的variables。 然后这些variables在周围的函数和函数文字之间被共享,并且只要它们是可访问的,它们就存活下来。
一个函数literal可以被分配给一个variables或直接调用。
呼叫
给定函数types
F
的expression式f
,f(a1, a2, … an)
用参数
a1, a2, … an
调用f
。在函数调用中,函数值和参数按通常的顺序进行评估。 在它们被评估之后,调用的参数被传递给函数,并且被调用的函数开始执行。 当函数返回时,函数的返回参数被传递给调用函数。
延迟陈述
“
defer
”语句调用执行延迟到周围函数返回时的函数。DeferStmt = "defer" Expression .
expression式必须是函数或方法调用。 每次执行“
defer
”语句时,调用的函数值和参数将照常进行计算并重新保存,但实际函数不会被调用。 相反,延迟调用紧接在周围函数返回之前,返回值(如果有的话)已经被评估之后,但在它们被返回给调用者之前,以LIFO顺序执行。
既然你还是感到困惑,这里是另一个试图提供你的问题的答案。
在你的问题的上下文中, ()
是函数调用操作符。
例如,函数文字
func(i int) int { return 42 * i }
代表一个匿名function。
()
函数调用操作符之后的函数literal
func(i int) int { return 42 * i }(7)
代表一个直接被调用的匿名函数。
通常,在函数调用中,函数值和参数按通常的顺序进行评估。 在它们被评估之后,调用的参数被传递给函数,并且被调用的函数开始执行。 当函数返回时,函数的返回参数被传递给调用函数。
但是,通过延迟语句调用函数是一个特例。 每次执行“延迟”语句时,调用的函数值和参数将照常进行计算并重新保存,但实际函数不会被调用。 相反,延迟调用紧接在周围函数返回之前,返回值(如果有的话)已经被评估之后,但在它们被返回给调用者之前,以LIFO顺序执行。
defer语句expression式必须是直接调用的函数或方法调用,而不是直接调用的函数或方法文字。 因此,函数或方法字面量需要由()
函数调用操作符跟随,以便延迟语句expression式是函数或方法调用。
推迟声明
defer func(i int) int { return 42 * i }(7)
已validation。
推迟声明
defer func(i int) int { return 42 * i }
无效: syntax error: argument to go/defer must be function call
。