Direkt zum Hauptinhalt springen
eLearner.app
Modul 3 · Lektion 5 von 515/50 im Kurs~12 min
Lektionen des Moduls (5/5)

defer: garantierte Bereinigung

defer verschiebt die Ausführung eines Aufrufs bis zur umschließenden Funktion kehrt zurück. Es wird auch dann ausgeführt, wenn die Funktion per Panik beendet wird. Mehrere defers in derselben Funktion in LIFO-Reihenfolge ausführen (Last-In, First-Out).

Es ist der idiomatische Mechanismus für die Ressourcenbereinigung: Dateien schließen, Mutexe entsperren, Verbindungen freigeben, Zustand wiederherstellen.

Syntax und kanonischer Anwendungsfall

Go
f, err := os.Open(path)
if err != nil {
    return err
}
defer f.Close()   // chiamata SEMPRE all'uscita della funzione

// ... usa f liberamente ...
return nil

Typisch: f.Close(), mu.Unlock(), db.Close(), tx.Rollback(), recover().

Argumente sofort ausgewertet, Ausführung verschoben

Wenn Sie defer foo(x) schreiben, wird x zum Zeitpunkt des ausgewertet defer, nicht zum Ausführungszeitpunkt:

Go
x := 10
defer fmt.Println(x)   // cattura 10
x = 99
// quando la funzione ritorna stampa "10", non "99"

LIFO-Auftrag

Verschiebt das Stapeln und Ausführen, beginnend mit dem aktuellsten:

Go
defer fmt.Println(1)
defer fmt.Println(2)
defer fmt.Println(3)
// output: 3, 2, 1

Dies steht im Einklang mit der Ressourcensemantik: Das zuletzt Geöffnete ist das Erste geschlossen werden.

defer + recover: Umgang mit Panik

defer ist die einzige Möglichkeit, einen panic abzufangen:

Go
func safe() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("recuperato:", r)
        }
    }()
    panic("boom")
}

Fallstrick: defer in einer Schleife

Wenn Sie defer f.Close() in eine Schleife einfügen, werden Verzögerungen bis zum Ende akkumuliert die Funktion**, nicht jeder Iteration. In einer langen Schleife öffnen Sie viele Dateien und schließen Sie sie erst, wenn die Funktion beendet wird: Es besteht die Gefahr eines Dateideskriptorlecks.

Go
// MALE: defer accumulati
for _, path := range paths {
    f, _ := os.Open(path)
    defer f.Close()   // chiude TUTTI alla fine della funzione esterna
    // ...
}

// MEGLIO: estrai in una funzione
for _, path := range paths {
    elabora(path)   // ogni chiamata ha il proprio scope di defer
}

Probieren Sie es aus

Übung#go.m3.l5.e1
Versuche: 0Wird geladen…

Geben Sie „inizio“ ein, verzögern Sie dann den Ausdruck von „fine“ mit „defer“ und geben Sie dann „mezzo“ aus. Die Ausgabe muss sein: inizio / mezzo / fine.

Editor wird geladen…
Hinweis anzeigen

Die Verzögerung wird ausgeführt, wenn main() kurz vor der Rückkehr steht.

Lösung nach 3 Versuchen verfügbar

Übung#go.m3.l5.e2
Versuche: 0Wird geladen…

Schreiben Sie drei aufeinanderfolgende Verzögerungen, die 1, 2 und 3 in dieser Reihenfolge im Code ausgeben. Die Ausgabe erfolgt 3/2/1 (LIFO).

Editor wird geladen…
Hinweis anzeigen

Drei Aufschübe nacheinander; Denken Sie daran, dass sie in umgekehrter Reihenfolge ausgeführt werden.

Lösung nach 3 Versuchen verfügbar

Quiz#go.m3.l5.e3
Bereit

Was wird hier gedruckt? (Hinweis: Verzögerungsargumente werden sofort ausgewertet)

Go
x := 10
defer fmt.Println(x)
x = 99
Antwortoptionen

Zusammenfassung

  • defer call() verschiebt die Ausführung, bis die Funktion zurückkehrt.
  • Mehrere Verzögerungen = LIFO-Reihenfolge.
  • Argumente sofort bewertet, Ausführung verschoben. – Wird zur deterministischen Bereinigung verwendet: Dateien, Mutexe, Verbindungen.
  • defer + recover() fängt einen panic ab (selten, an Grenzen).
  • Niemals defer innerhalb einer langen Schleife: Es sammelt sich an, bis die äußere Funktion zurückkehrt.