Przejdź do głównej treści
eLearner.app
Moduł 3 · Lekcja 5 z 515/50 w kursie~12 min
Lekcje modułu (5/5)

`defer`: gwarantowane czyszczenie

defer opóźnia wykonanie wywołania ** do czasu załączenia funkcji zwraca**. Działa nawet jeśli funkcja zakończy się w wyniku paniki. Wiele defer w tej samej funkcji wykonaj w kolejności LIFO (ostatnie weszło, pierwsze wyszło).

Jest to idiomatyczny mechanizm czyszczenia zasobów: zamykania plików, odblokowywanie muteksów, zwalnianie połączeń, przywracanie stanu.

Składnia i kanoniczny przypadek użycia

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

Typowe: f.Close(), mu.Unlock(), db.Close(), tx.Rollback(), KODEF4.

Argumenty oceniane natychmiast, wykonanie odroczone

Kiedy piszesz defer foo(x), x jest oceniany w momencie defer, nie w momencie wykonania:

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

Kolejność LIFO

Odracza układanie i wykonywanie, zaczynając od najnowszego:

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

Jest to zgodne z semantyką zasobów: ostatni otwarty jest pierwszym być zamknięte.

defer + recover: obsługa paniki

defer to jedyny sposób na przechwycenie panic:

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

Pułapka: defer w pętli

Umieszczenie defer f.Close() w pętli kumuluje się, odracza aż do końca funkcji, a nie każdej iteracji. W długiej pętli otwierasz wiele plików i zamykaj je dopiero po zakończeniu funkcji: ryzyko wycieku deskryptora pliku.

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
}

Spróbuj

Ćwiczenie#go.m3.l5.e1
Próby: 0Ładowanie...

Wydrukuj „inizio”, następnie odrocz wydruk „fine” za pomocą odroczenia, a następnie wydrukuj „mezzo”. Wynik musi brzmieć: inizio / mezzo / fine.

Ładowanie edytora...
Pokaż wskazówkę

Odroczenie jest uruchamiane, gdy funkcja main() ma wkrótce powrócić.

Rozwiązanie dostępne po 3 próbach

Ćwiczenie#go.m3.l5.e2
Próby: 0Ładowanie...

Zapisz trzy kolejne odroczenia, które wypiszą w kodzie cyfry 1, 2 i 3 w tej kolejności. Wyjście będzie wynosić 3/2/1 (LIFO).

Ładowanie edytora...
Pokaż wskazówkę

Trzy odroczenia po kolei; pamiętaj, że wykonują się w odwrotnej kolejności.

Rozwiązanie dostępne po 3 próbach

Quiz#go.m3.l5.e3
Gotowe

Co to drukuje? (uwaga: argumenty odroczenia są oceniane natychmiast)

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

Podsumowanie

  • defer call() opóźnia wykonanie do czasu powrotu funkcji.
  • Wiele odroczeń = porządek LIFO.
  • Argumenty rozpatrywane natychmiast, wykonanie odroczone.
  • Używany do deterministycznego czyszczenia: plików, muteksów, połączeń.
  • defer + recover() przechwytuje panic (rzadko, na granicach).
  • Nigdy nie używaj defer w długiej pętli: gromadzi się do momentu powrotu funkcji zewnętrznej.