Gestione Errori
Go non ha eccezioni runtime. La sua filosofia è che gli eventuali errori devono essere verificati e gestiti ad ogni passo.
Sappiamo che molte funzioni di libreria ritornano un errore come ultimo valore. E' opportuno che anche le nostre funzioni si comportino in modo simile.
Esempio
(210errors.go):
package main
// Modulo per la gestione errori
// 'error' è un'interfaccia
import "errors"
import "fmt"
// E' convenzione ritornare un errore come ultimo valore
func f1(arg int) (int, error) {
if arg == 42 {
// `errors.New` costruisce un nuovo messaggio d'errore
return -1, errors.New("can't work with 42")
}
// 'nil' indica l'assenza d'errore
return arg + 3, nil
}
// E' possibile definire un tipo custom per gli errori
type argError struct {
arg int
prob string
}
// Per soddisfare l'interfaccia 'error' occorre
// implementare il metodo Error
func (e *argError) Error() string {
return fmt.Sprintf("%d - %s", e.arg, e.prob)
}
func f2(arg int) (int, error) {
if arg == 42 {
// Occorre costruire una struct &argError
return -1, &argError{arg, "can't work with it"}
}
return arg + 3, nil
}
func main() {
// Test con 'error' standard
for _, i := range []int{7, 42} {
// Notare l'assegnazione e if combinati
if r, e := f1(i); e != nil {
fmt.Println("f1 failed:", e)
} else {
fmt.Println("f1 worked:", r)
}
}
// Test con 'argError'
for _, i := range []int{7, 42} {
if r, e := f2(i); e != nil {
fmt.Println("f2 failed:", e)
} else {
fmt.Println("f2 worked:", r)
}
}
// Per usare i dati in un gestore custom
_, e := f2(42)
// Occorre un puntatore *argError perche il nostro f2
// genera un indirizzo &argError
if ae, ok := e.(*argError); ok {
fmt.Println(ae.arg)
fmt.Println(ae.prob)
}
}