Ce face panic și recover
panic(v) oprește imediat funcția curentă și începe să „desfacă” stiva: rulează funcțiile amânate cu defer ale fiecărei funcții, de jos în sus. Dacă nimeni nu oprește panic-ul, programul se încheie cu mesajul panic: ..., stiva de apeluri și codul de ieșire 2. Go însuși provoacă panic la erori de programare: index în afara slice-ului, pointer nil, împărțire întreagă la zero, scriere într-un map nil.
recover() oprește un panic și returnează valoarea dată lui panic. Funcționează doar dacă este apelat direct într-o funcție amânată cu defer; în rest returnează nil. După recover, funcția în care a apărut panic-ul se termină normal, cu valorile rezultat de atunci.
panic nu înlocuiește erorile: problemele obișnuite (fișier lipsă, date greșite) se returnează ca error. Panic se folosește pentru situații imposibile sau erori de programare. Un server web folosește recover ca o cerere cu probleme să nu oprească tot serverul.
Parametri
| Parametru | Descriere |
|---|---|
v |
Orice valoare (de obicei un text sau un error); este returnată de recover și afișată dacă programul se oprește. |
Ce returnează
panic nu se întoarce niciodată. recover returnează valoarea panic-ului sau nil dacă nu există panic.
Exemple
Exemplul 1: Transformarea unui panic în eroare
Împărțirea la zero cu întregi provoacă panic; recover din defer îl transformă în error prin rezultatul numit.
package main
import "fmt"
func imparte(a, b int) (rezultat int, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("calcul eșuat: %v", r)
}
}()
return a / b, nil
}
func main() {
fmt.Println(imparte(10, 3))
fmt.Println(imparte(10, 0))
fmt.Println("programul continuă")
}
Rezultat
3 <nil>
0 calcul eșuat: runtime error: integer divide by zero
programul continuă
Exemplul 2: Ordinea: defer rulează, apoi recover
package main
import "fmt"
func proceseaza(comenzi []string, i int) {
defer fmt.Println(" curățenie pentru comanda", i)
fmt.Println(" procesez", comenzi[i])
}
func main() {
comenzi := []string{"pizza", "salată"}
defer func() {
if r := recover(); r != nil {
fmt.Println("recuperat:", r)
}
}()
for i := range 3 {
proceseaza(comenzi, i)
}
fmt.Println("nu se mai ajunge aici")
}
Rezultat
procesez pizza
curățenie pentru comanda 0
procesez salată
curățenie pentru comanda 1
curățenie pentru comanda 2
recuperat: runtime error: index out of range [2] with length 2
Exemplul 3: Panic propriu pentru o situație imposibilă
Un panic necapturat oprește programul cu codul 2 și afișează stiva de apeluri.
package main
import "fmt"
func zi(n int) string {
zile := []string{"luni", "marți", "miercuri", "joi", "vineri", "sâmbătă", "duminică"}
if n < 1 || n > 7 {
panic(fmt.Sprintf("zi invalidă: %d", n))
}
return zile[n-1]
}
func main() {
fmt.Println(zi(3))
fmt.Println(zi(9))
}
Rezultat
miercuri
panic: zi invalidă: 9
goroutine 1 [running]:
main.zi(0x104a12c18?)
/home/ana/proiect/main.go:8 +0xf0
main.main()
/home/ana/proiect/main.go:15 +0x5c
exit status 2
Greșeli frecvente
- Folosirea lui
panicpentru erori obișnuite, ca o excepție: în Go se returneazăerror. recover()apelat direct în funcție, nu într-undefer, sau într-o funcție apelată de funcția amânată: returneazănilși nu oprește nimic.recoverîntr-o goroutine nu oprește panic-ul altei goroutine: fiecare goroutine își are propriuldefer/recover.
Comenzi înrudite
defer: Amână un apel de funcție până la ieșirea din funcția curentă; folosit pentru închideri și curățenie.erori (error): În Go erorile sunt valori: funcțiile returnează un error, iar apelantul verifică if err != nil.go și goroutine: Cuvântul go pornește o funcție într-o goroutine: un fir de execuție ușor, care rulează în paralel cu restul programului.