Ce face defer
defer programează un apel de funcție care rulează când funcția curentă se termină: după return, la final sau chiar la panic. Se folosește pentru curățenie scrisă imediat lângă deschidere: f, err := os.Open(...) urmat de defer f.Close(), mu.Lock() urmat de defer mu.Unlock().
Mai multe defer rulează în ordine inversă (ultimul amânat rulează primul), ca o stivă. Argumentele apelului amânat se evaluează imediat, în momentul lui defer, nu la final.
defer funcționează la nivel de funcție, nu de bloc: un defer într-o buclă rulează abia la ieșirea din funcție, nu la fiecare iterație. Împreună cu recover, un defer poate opri un panic.
Exemple
Exemplul 1: Ordinea inversă și argumentele evaluate imediat
i este copiat în momentul lui defer, deci se afișează 2, 1, 0.
package main
import "fmt"
func main() {
fmt.Println("început")
for i := range 3 {
defer fmt.Println("defer", i)
}
fmt.Println("sfârșit")
}
Rezultat
început
sfârșit
defer 2
defer 1
defer 0
Exemplul 2: Închiderea unui fișier
defer f.Close() stă imediat după deschidere; fișierul se închide oricum s-ar termina funcția.
package main
import (
"bufio"
"fmt"
"os"
)
func numaraRanduri(cale string) (int, error) {
f, err := os.Open(cale)
if err != nil {
return 0, err
}
defer f.Close()
n := 0
sc := bufio.NewScanner(f)
for sc.Scan() {
n++
}
return n, sc.Err()
}
func main() {
if err := os.WriteFile("lista.txt", []byte("pâine\nlapte\nouă\n"), 0o644); err != nil {
fmt.Println(err)
return
}
n, err := numaraRanduri("lista.txt")
fmt.Println("rânduri:", n, "eroare:", err)
_, err = numaraRanduri("lipsa.txt")
fmt.Println("eroare:", err)
}
Rezultat
rânduri: 3 eroare: <nil>
eroare: open lipsa.txt: no such file or directory
Exemplul 3: Măsurarea duratei unei funcții
Durata reală diferă la fiecare rulare; aici funcția „durează” un număr fix de pași, ca rezultatul să fie același.
package main
import "fmt"
func urmareste(nume string) func() {
fmt.Println("intru în", nume)
return func() { fmt.Println("ies din", nume) }
}
func calcul() int {
defer urmareste("calcul")() // primul apel rulează acum, al doilea la final
suma := 0
for i := 1; i <= 100; i++ {
suma += i
}
fmt.Println("suma calculată:", suma)
return suma
}
func main() {
fmt.Println("rezultat:", calcul())
}
Rezultat
intru în calcul
suma calculată: 5050
ies din calcul
rezultat: 5050
Greșeli frecvente
defer f.Close()înainte de verificarea erorii de laos.Open: dacăfestenil, închiderea dă panic. Verifică întâierr.deferîntr-o buclă care deschide multe fișiere: toate rămân deschise până la finalul funcției. Mută corpul buclei într-o funcție separată.- Așteptarea ca argumentele să fie citite la final:
defer fmt.Println(x)afișează valoarea luixdin momentul luidefer.
Comenzi înrudite
panic și recover: panic oprește execuția normală pentru erori grave; recover, apelat într-un defer, poate opri panic-ul.func: Declară o funcție: parametri cu tip după nume, unul sau mai multe rezultate, metode și funcții anonime.return: Încheie funcția curentă și trimite apelantului valorile rezultat (zero, una sau mai multe).