53

我正在阅读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
}

我不清楚在闭包体之后添加和使用“()”的原因,希望有人能解释清楚。

4

4 回答 4

75

不是()必须(仅defer. defer 语句的语言规范要求其“表达式”始终必须是函数调用。

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

考虑:

func f() int { return 42 }

a := f

对比

b := f()

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

的语义也是如此:

defer f

对比

defer f()

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

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

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

for i := range whatever {
        defer func() { fmt. Println(i) }()
}

for i := range whatever {
        defer func(n int) { fmt. Println(n) }(i)
}

第一个版本在闭包执行时打印“i”的值,第二个版本在执行 defer 语句时打印“i”的

于 2013-04-15T06:17:43.520 回答
18

如果您不想阅读冗长的答案:

str := "Alice"
go func(name string) {
    fmt.Println("Your name is", name)
}(str)

等同于:

str := "Alice"
f := func(name string) {
    fmt.Println("Your name is", name)
}
go f(str)
于 2019-02-18T10:44:00.890 回答
16

参考

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

于 2013-04-15T12:43:10.097 回答
0

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

于 2019-10-09T19:10:09.840 回答