Go
La gestion des erreurs
Recherche…
Introduction
Dans Go, les situations inattendues sont traitées à l'aide d' erreurs et non d'exceptions. Cette approche est plus similaire à celle de C, en utilisant errno, qu’à celle de Java ou d’autres langages orientés objet, avec leurs blocs try / catch. Cependant, une erreur n'est pas un entier mais une interface.
Une fonction susceptible d'échouer renvoie généralement une erreur comme dernière valeur de retour. Si cette erreur n'est pas nulle , quelque chose ne va pas et l'appelant de la fonction doit agir en conséquence.
Remarques
Notez comment dans Go vous ne soulevez pas d'erreur. Au lieu de cela, vous renvoyez une erreur en cas d'échec.
Si une fonction peut échouer, la dernière valeur renvoyée est généralement un type d' error
.
// This method doesn't fail
func DoSomethingSafe() {
}
// This method can fail
func DoSomething() (error) {
}
// This method can fail and, when it succeeds,
// it returns a string.
func DoAndReturnSomething() (string, error) {
}
Créer une valeur d'erreur
Le moyen le plus simple de créer une erreur consiste à utiliser le package d’ errors
.
errors.New("this is an error")
Si vous souhaitez ajouter des informations supplémentaires à une erreur, le package fmt
fournit également une méthode de création d'erreur utile:
var f float64
fmt.Errorf("error with some additional information: %g", f)
Voici un exemple complet, où l'erreur est renvoyée par une fonction:
package main
import (
"errors"
"fmt"
)
var ErrThreeNotFound = errors.New("error 3 is not found")
func main() {
fmt.Println(DoSomething(1)) // succeeds! returns nil
fmt.Println(DoSomething(2)) // returns a specific error message
fmt.Println(DoSomething(3)) // returns an error variable
fmt.Println(DoSomething(4)) // returns a simple error message
}
func DoSomething(someID int) error {
switch someID {
case 3:
return ErrThreeNotFound
case 2:
return fmt.Errorf("this is an error with extra info: %d", someID)
case 1:
return nil
}
return errors.New("this is an error")
}
Créer un type d'erreur personnalisé
Dans Go, une erreur est représentée par toute valeur pouvant se décrire comme une chaîne. Tout type qui implémente l'interface d' error
intégrée est une erreur.
// The error interface is represented by a single
// Error() method, that returns a string representation of the error
type error interface {
Error() string
}
L'exemple suivant montre comment définir un nouveau type d'erreur à l'aide d'un littéral composé de chaînes.
// Define AuthorizationError as composite literal
type AuthorizationError string
// Implement the error interface
// In this case, I simply return the underlying string
func (e AuthorizationError) Error() string {
return string(e)
}
Je peux maintenant utiliser mon type d'erreur personnalisé comme erreur:
package main
import (
"fmt"
)
// Define AuthorizationError as composite literal
type AuthorizationError string
// Implement the error interface
// In this case, I simply return the underlying string
func (e AuthorizationError) Error() string {
return string(e)
}
func main() {
fmt.Println(DoSomething(1)) // succeeds! returns nil
fmt.Println(DoSomething(2)) // returns an error message
}
func DoSomething(someID int) error {
if someID != 1 {
return AuthorizationError("Action not allowed!")
}
// do something here
// return a nil error if the execution succeeded
return nil
}
Renvoyer une erreur
Dans Allez vous ne soulevez pas d'erreur. Au lieu de cela, vous renvoyez une error
en cas d'échec.
// This method can fail
func DoSomething() error {
// functionThatReportsOK is a side-effecting function that reports its
// state as a boolean. NOTE: this is not a good practice, so this example
// turns the boolean value into an error. Normally, you'd rewrite this
// function if it is under your control.
if ok := functionThatReportsOK(); !ok {
return errors.New("functionThatReportsSuccess returned a non-ok state")
}
// The method succeeded. You still have to return an error
// to properly obey to the method signature.
// But in this case you return a nil error.
return nil
}
Si la méthode retourne plusieurs valeurs (et que l'exécution peut échouer), la convention standard consiste à renvoyer l'erreur comme dernier argument.
// This method can fail and, when it succeeds,
// it returns a string.
func DoAndReturnSomething() (string, error) {
if os.Getenv("ERROR") == "1" {
return "", errors.New("The method failed")
}
s := "Success!"
// The method succeeded.
return s, nil
}
result, err := DoAndReturnSomething()
if err != nil {
panic(err)
}
Gérer une erreur
Les erreurs In Go peuvent être renvoyées par un appel de fonction. La convention est que si une méthode peut échouer, le dernier argument renvoyé est une error
.
func DoAndReturnSomething() (string, error) {
if os.Getenv("ERROR") == "1" {
return "", errors.New("The method failed")
}
// The method succeeded.
return "Success!", nil
}
Vous utilisez plusieurs affectations de variables pour vérifier si la méthode a échoué.
result, err := DoAndReturnSomething()
if err != nil {
panic(err)
}
// This is executed only if the method didn't return an error
fmt.Println(result)
Si l'erreur ne vous intéresse pas, vous pouvez simplement l'ignorer en l'attribuant à _
.
result, _ := DoAndReturnSomething()
fmt.Println(result)
Bien sûr, ignorer une erreur peut avoir de graves conséquences. Par conséquent, ceci n'est généralement pas recommandé.
Si vous avez plusieurs appels de méthode et qu'une ou plusieurs méthodes de la chaîne peuvent renvoyer une erreur, vous devez propager l'erreur au premier niveau capable de la gérer.
func Foo() error {
return errors.New("I failed!")
}
func Bar() (string, error) {
err := Foo()
if err != nil {
return "", err
}
return "I succeeded", nil
}
func Baz() (string, string, error) {
res, err := Bar()
if err != nil {
return "", "", err
}
return "Foo", "Bar", nil
}
Récupérer de la panique
Une erreur courante est de déclarer une tranche et de commencer à demander des index sans l'initialiser, ce qui conduit à une panique "d'index hors de portée". Le code suivant explique comment se remettre de la panique sans quitter le programme, ce qui est le comportement normal d'une panique. Dans la plupart des cas, le fait de renvoyer une erreur de cette manière plutôt que de quitter le programme en panique n'est utile que pour le développement ou les tests.
type Foo struct {
Is []int
}
func main() {
fp := &Foo{}
if err := fp.Panic(); err != nil {
fmt.Printf("Error: %v", err)
}
fmt.Println("ok")
}
func (fp *Foo) Panic() (err error) {
defer PanicRecovery(&err)
fp.Is[0] = 5
return nil
}
func PanicRecovery(err *error) {
if r := recover(); r != nil {
if _, ok := r.(runtime.Error); ok {
//fmt.Println("Panicing")
//panic(r)
*err = r.(error)
} else {
*err = r.(error)
}
}
}
L'utilisation d'une fonction distincte (plutôt que la fermeture) permet de réutiliser la même fonction dans d'autres fonctions sujettes à la panique.