Recherche…
Introduction
Une instruction de defer
pousse un appel de fonction sur une liste. La liste des appels enregistrés est exécutée après le retour de la fonction environnante. Defer est couramment utilisé pour simplifier les fonctions qui effectuent diverses actions de nettoyage.
Syntaxe
- différer someFunc (args)
- reporter func () {// le code va ici} ()
Remarques
Defer fonctionne en injectant un nouveau frame de pile (la fonction appelée après le mot-clé defer
) dans la pile d'appels située sous la fonction en cours d'exécution. Cela signifie que le renvoi est garanti tant que la pile sera déroulée (si votre programme plante ou obtient un SIGKILL
, le différé ne sera pas exécuté).
Différer les bases
Une déclaration différée dans Go est simplement un appel de fonction marqué pour être exécuté ultérieurement. L'instruction de report est un appel de fonction ordinaire préfixé par le mot-clé defer
.
defer someFunction()
Une fonction différée est exécutée une fois que la fonction qui contient la déclaration de defer
est retournée. L'appel réel à la fonction différée se produit lorsque la fonction englobante:
- exécute une déclaration de retour
- tombe la fin
- panique
Exemple:
func main() {
fmt.Println("First main statement")
defer logExit("main") // position of defer statement here does not matter
fmt.Println("Last main statement")
}
func logExit(name string) {
fmt.Printf("Function %s returned\n", name)
}
Sortie:
First main statement
Last main statement
Function main returned
Si une fonction comporte plusieurs instructions différées, elles forment une pile. Le dernier defer
est le premier à s'exécuter après le retour de la fonction englobante, suivi des appels suivants au defer
précédent dans l'ordre (l'exemple ci-dessous renvoie en provoquant une panique):
func main() {
defer logNum(1)
fmt.Println("First main statement")
defer logNum(2)
defer logNum(3)
panic("panic occurred")
fmt.Println("Last main statement") // not printed
defer logNum(3) // not deferred since execution flow never reaches this line
}
func logNum(i int) {
fmt.Printf("Num %d\n", i)
}
Sortie:
First main statement
Num 3
Num 2
Num 1
panic: panic occurred
goroutine 1 [running]:
....
Notez que les fonctions différées ont leurs arguments évalués au moment defer
exécute:
func main() {
i := 1
defer logNum(i) // deferred function call: logNum(1)
fmt.Println("First main statement")
i++
defer logNum(i) // deferred function call: logNum(2)
defer logNum(i*i) // deferred function call: logNum(4)
return // explicit return
}
func logNum(i int) {
fmt.Printf("Num %d\n", i)
}
Sortie:
First main statement
Num 4
Num 2
Num 1
Si une fonction a des valeurs de retour nommées, une fonction anonyme différée au sein de cette fonction peut accéder à la valeur renvoyée et la mettre à jour même après le retour de la fonction:
func main() {
fmt.Println(plusOne(1)) // 2
return
}
func plusOne(i int) (result int) { // overkill! only for demonstration
defer func() {result += 1}() // anonymous function must be called by adding ()
// i is returned as result, which is updated by deferred function above
// after execution of below return
return i
}
Enfin, une déclaration defer
est généralement utilisée pour des opérations qui se produisent souvent ensemble. Par exemple:
- ouvrir et fermer un fichier
- connecter et déconnecter
- verrouiller et déverrouiller un mutex
- marquer un groupe d’attente comme fait (
defer wg.Done()
)
Cette utilisation garantit une libération correcte des ressources du système, quel que soit le flux d'exécution.
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close() // Body will always get closed
Appels de fonction différés
Appels de fonction différée jouent un rôle semblable à des choses comme finally
des blocs dans des langages comme Java: ils assurent que certaines fonction sera exécutée lorsque la fonction retourne externe, quel que soit le cas d' une erreur ou qui renvoient déclaration a été frappé dans les cas avec plusieurs retours. Ceci est utile pour nettoyer les ressources qui doivent être fermées comme les connexions réseau ou les pointeurs de fichiers. Le mot-clé defer
indique un appel de fonction différé, de la même manière que le mot go
clé go
initiant une nouvelle goroutine. Comme un go
appel, des arguments sont évalués immédiatement, mais contrairement à un go
appel, les fonctions différées ne sont pas exécutées simultanément.
func MyFunc() {
conn := GetConnection() // Some kind of connection that must be closed.
defer conn.Close() // Will be executed when MyFunc returns, regardless of how.
// Do some things...
if someCondition {
return // conn.Close() will be called
}
// Do more things
}// Implicit return - conn.Close() will still be called
Notez l'utilisation de conn.Close()
au lieu de conn.Close
- vous ne faites pas que passer une fonction, vous différerez un appel de fonction complet, y compris ses arguments. Plusieurs appels de fonction peuvent être différés dans la même fonction externe et chacun sera exécuté une fois dans l'ordre inverse. Vous pouvez également reporter les fermetures - n'oubliez pas les parens!
defer func(){
// Do some cleanup
}()