为了账号安全,请及时绑定邮箱和手机立即绑定

为什么在 Golang 的闭包体后面加“()”?

为什么在 Golang 的闭包体后面加“()”?

Go
凤凰求蛊 2021-06-03 22:45:35
我正在阅读The Go Programming Language Specifications,发现自己并没有真正理解闭包体之后的“()”:在Function literals:func(ch chan int) { ch <- ACK } (replyChan) `在Defer statements的例子中:// f returns 1func f() (result int) {    defer func() {        result++    }() // why and how?    return 0}我不清楚在闭包主体后添加和使用“()”的原因,希望有人能解释清楚。
查看完整描述

4 回答

?
开满天机

TA贡献1786条经验 获得超13个赞

这并不是说()必须(仅)一个后添加封在defer。defer 语句的语言规范要求其“表达式”始终必须是函数调用。


为什么会这样?它与任何其他函数相同,无论是否“延迟”:


考虑:


func f() int { return 42 }


a := f

对比


b := f()

第一个表达式 RHS 是一个函数值。在第二个版本中,RHS 是函数返回的值——即函数调用。


语义也是如此:


defer f

对比


defer f()

除了第一个版本在“延迟”的上下文中没有意义,因此规范提到它必须是第二种形式(仅)。


恕我直言,由于与“延迟”语句之外的上述函数调用的正交性,因此也更容易学习。


另请注意,函数调用不仅是 fn-expr 后跟(),而且表达式列表通常位于括号内(包括空列表)。之间有很大的区别:


for i := range whatever {

        defer func() { fmt. Println(i) }()

}


for i := range whatever {

        defer func(n int) { fmt. Println(n) }(i)

}

关闭时,第一个版本在当下打印的“我”的值执行,第二打印“我”在当下的价值时,defer语句被执行。


查看完整回答
反对 回复 2021-06-07
?
杨__羊羊

TA贡献1943条经验 获得超7个赞

参考

Go 编程语言规范

函数类型

函数类型表示具有相同参数和结果类型的所有函数的集合。

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 .

函数字面量

函数文字表示匿名函数。它由函数类型的规范和函数体组成。

FunctionLit = FunctionType Body .

函数字面量是闭包:它们可以引用在周围函数中定义的变量。然后这些变量在周围的函数和函数字面量之间共享,只要它们可访问,它们就会存在。

函数文字可以分配给变量或直接调用。

通话

给定一个f函数类型的表达式F

f(a1, a2, … an)

f带参数的调用a1, a2, … an

在函数调用中,函数值和参数按通常的顺序计算。在对它们求值后,调用的参数按值传递给函数,被调用的函数开始执行。当函数返回时,函数的返回参数按值传递回调用函数。

推迟声明

defer" 语句调用一个函数,该函数的执行被推迟到周围函数返回的那一刻。

DeferStmt = "defer" Expression .

表达式必须是函数或方法调用。每次“ defer”语句执行时,函数值和调用的参数都会像往常一样被评估并重新保存,但不会调用实际的函数。相反,延迟调用在周围函数返回之前立即以 LIFO 顺序执行,在返回值(如果有)被评估之后,但在它们返回给调用者之前。

由于您仍然感到困惑,这是为您的问题提供答案的另一种尝试。

在您的问题的上下文中,()是函数调用运算符。

例如,函数字面量

func(i int) int { return 42 * i }

表示匿名函数。

函数文字后跟()函数调用运算符

func(i int) int { return 42 * i }(7)

表示一个匿名函数,然后直接调用该函数。

通常,在函数调用中,函数值和参数按通常的顺序计算。在对它们求值后,调用的参数按值传递给函数,被调用的函数开始执行。当函数返回时,函数的返回参数按值传递回调用函数。

但是,通过 defer 语句调用函数是一种特殊情况。每次执行“defer”语句时,函数值和调用的参数都会像往常一样评估并重新保存,但不会调用实际的函数。相反,延迟调用在周围函数返回之前立即以 LIFO 顺序执行,在返回值(如果有)被评估之后,但在它们返回给调用者之前。

defer 语句表达式必须是直接调用的函数或方法调用,而不仅仅是未直接调用的函数或方法文字。因此,函数或方法字面量后面需要跟()函数调用运算符,这样 defer 语句表达式就是函数或方法调用。

延迟声明

defer func(i int) int { return 42 * i }(7)

已验证。

延迟声明

defer func(i int) int { return 42 * i }

无效:syntax error: argument to go/defer must be function call


查看完整回答
反对 回复 2021-06-07
?
慕虎7371278

TA贡献1802条经验 获得超4个赞

如果没有 (),您就不会执行该函数。


查看完整回答
反对 回复 2021-06-07
  • 4 回答
  • 0 关注
  • 373 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信