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
}()


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow