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)
	}
}