web-dev-qa-db-fra.com

Pourquoi ajouter "()" après fermeture du corps à Golang?

Je lis The Go Programming Language Specifications et je ne me suis pas vraiment compris avec "()" après la fermeture du corps:

Dans Function literals:

func (ch chan int) {ch <- ACK} (réponseChan) `

Dans l'exemple Defer statements:

// f returns 1
func f() (result int) {
    defer func() {
        result++
    }() // why and how?
    return 0
}

Je ne suis pas clair sur la raison d'ajouter & l'utilisation de "()" après la fermeture du corps, j'espère que quelqu'un pourra l'expliquer clairement.

40
Reck Hou

Ce n'est pas que () doit être ajouté après (seulement) un fermeture dans defer. Les spécifications de langue pour la instruction différée exigent que son expression toujours soit un appel de fonction.

Et pourquoi en est-il ainsi? C'est la même chose que pour n'importe quelle autre fonction, avec ou sans report:

Considérer:

func f() int { return 42 }

et

a := f

contre

b := f()

La première expression RHS est une valeur de fonction. Dans la deuxième version, le RHS est la valeur renvoyée par la fonction - c'est-à-dire un appel de fonction.

La sémantique de:

defer f

contre

defer f()

sauf que la première version n'a pas de sens dans le contexte de 'différer', et donc les spécifications mentionnent qu'il doit s'agir de la seconde forme (seulement).

C'est aussi plus facile à apprendre en raison de l'orthogonalité avec l'appel de fonction décrit ci-dessus en dehors de l'instruction 'différer'.

Notez également qu'un appel de fonction n'est pas seulement fn-expr suivi de (), mais qu'une liste d'expressions est généralement comprise entre parenthèses (y compris une liste vide). Il y a une grande différence entre:

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

et 

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

La première version affiche la valeur de 'i' au moment où la fermeture exécute, la seconde imprime la valeur de 'i' au moment où l'instruction différée était exécutée.

61
zzzz

Références

La spécification du langage de programmation Go

Types de fonction

Un type de fonction désigne l'ensemble de toutes les fonctions ayant le même types de paramètre et résultat.

FunctionType   = "func" Signature .
Signature      = Parameters [ Result ] .
Result         = Parameters | Type .
Parameters     = "(" [ ParameterList [ "," ] ] ")" .
ParameterList  = ParameterDecl { "," ParameterDecl } .
ParameterDecl  = [ IdentifierList ] [ "..." ] Type .

Déclarations de fonction

Une déclaration de fonction lie un identifiant, le nom de la fonction, à un une fonction.

FunctionDecl = "func" FunctionName Signature [ Body ] .
FunctionName = identifier .
Body         = Block .

Littéraux de fonction

Un littéral de fonction représente une fonction anonyme. Il consiste en un spécification du type de fonction et d'un corps de fonction.

FunctionLit = FunctionType Body .

Les littéraux de fonction sont des fermetures: ils peuvent faire référence à des variables définies dans une fonction environnante. Ces variables sont ensuite partagées entre les fichiers fonction environnante et la fonction littérale, et ils survivent en tant que tant qu'ils sont accessibles. 

Un littéral de fonction peut être affecté à une variable ou invoqué directement. 

Appels

Étant donné une expression f de type de fonction F,

f(a1, a2, … an)

appelle f avec les arguments a1, a2, … an

Dans un appel de fonction, la valeur de la fonction et les arguments sont évalués dans l'ordre habituel. Après leur évaluation, les paramètres de l’appel sont passés par valeur à la fonction et la fonction appelée commence exécution. Les paramètres de retour de la fonction sont passés par valeur Retour à la fonction appelante lorsque la fonction retourne. 

Déclarations différées

Une instruction "defer" appelle une fonction dont l'exécution est différée au moment où la fonction environnante revient.

DeferStmt = "defer" Expression .

L'expression doit être un appel de fonction ou de méthode. A chaque fois le "defer", la valeur de la fonction et les paramètres sont exécutés. Les appels sont évalués comme d'habitude et enregistrés à nouveau, mais la fonction réelle est non invoqué. À la place, les appels différés sont exécutés dans l’ordre LIFO immédiatement avant le retour de la fonction environnante, après le retour les valeurs éventuelles ont été évaluées, mais avant qu’elles ne soient renvoyées à l'appelant.

Puisque vous êtes toujours confus, voici une autre tentative pour répondre à votre question.

() est l'opérateur d'appel de fonction dans le contexte de votre question.

Par exemple, la fonction littérale

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

représente une fonction anonyme.

Littéral de fonction suivi de l'opérateur d'invocation de fonction ()

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

représente une fonction anonyme qui est ensuite appelée directement.

Normalement, dans un appel de fonction, la valeur de la fonction et les arguments sont évalués dans l'ordre habituel. Après leur évaluation, les paramètres de l'appel sont passés valeur à la fonction et la fonction appelée commence son exécution. Les paramètres de retour de la fonction sont renvoyés valeur par valeur à la fonction appelante lors du retour de la fonction. 

Cependant, l'invocation de la fonction via l'instruction différer est un cas particulier. Chaque fois que l'instruction "différer" est exécutée, la valeur de la fonction et les paramètres de l'appel sont évalués comme d'habitude et enregistrés à nouveau, mais la fonction réelle n'est pas appelée. Au lieu de cela, les appels différés sont exécutés dans l'ordre LIFO immédiatement avant le retour de la fonction environnante, après l'évaluation des valeurs renvoyées, le cas échéant, mais avant leur renvoi à l'appelant. 

L'expression d'instruction de renvoi doit être un appel de fonction ou de méthode invoqué directement, pas simplement un littéral de fonction ou de méthode qui n'est pas invoqué directement. Par conséquent, le littéral de fonction ou de méthode doit être suivi de l'opérateur d'invocation de la fonction () afin que l'expression de l'instruction différée soit un appel de fonction ou de méthode.

La déclaration différée

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

est valable.

La déclaration différée

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

n'est pas valide: syntax error: argument to go/defer must be function call.

14
peterSO

Si vous ne voulez pas lire de longues réponses:

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

Est identique à:

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