// squelette d'une fonction qui renvoi une erreur
funcmafonction() error {
ifcondition {
returnerr// retourne l'erreur
}
returnnil// retourne nil qui équivaux à aucune erreur
}
// code utilisé pour appeler la fonction:
err:=mafonction()
iferr!=nil {
// si la fonction retourne une erreur, la variable sera différente de nil
}
Cependant, cette manière de faire à ces limites, par exemple il n’est pas possible de créer des erreurs personnalisées avec cette technique. Pour créer des erreurs personnalisées, il faut créer ce qui est appelé des sentienelles. Il s’agit de variables qui seront des erreurs et qui contiendront un message d’erreur.
Voici un exemple basique d’une sentinelle:
packagemainimport (
"errors""log")
var (
errConnexion = errors.New("Identifiant ou mot de passe invalide")
)
funcaccountConnection(usernamestring, passwordstring) error {
varvalidUsernamestring = "name"varvaildPasswordstring = "pwd"if (username!=validUsername) || (password!=vaildPassword) {
returnerrConnexion }
returnnil}
funcmain() {
err:=accountConnection("name1", "pwd")
iferr!=nil {
log.Fatal(err)
}
}
Dans l’exemple précédent, on crée simplement une nouvelle erreur, la variable est déclarée comme étant une variable globale. Une bonne pratique est de nommer les variables errors de la manière suivante: ErrNomErreur, dans l’exemple je l’ai appelée errConnexion ce qui permet de rendre la variable seulement accessible au niveau du package.
Tester une erreur retournée
Pour comparer une erreur retournée, on peux tester simplement avec le code suivant:
iferr==errConnexion {
// il s'agit d'une erreur de connexion, on la traite
}
Ce type de code peux provoquer des problèmes, en reprendre l’exemple de la sentinelle, voici un exemple qui intègre une bonne pratique:
packagemainimport (
"errors""log")
var (
errConnexion = errors.New("Identifiant ou mot de passe invalide")
)
funcaccountConnection(usernamestring, passwordstring) error {
varvalidUsernamestring = "name"varvaildPasswordstring = "pwd"if (username!=validUsername) || (password!=vaildPassword) {
returnerrConnexion }
returnnil}
funcmain() {
err:=accountConnection("name1", "pwd")
iferr!=nil {
iferrors.Is(err, errConnexion) { // ici, on test si l'erreur est bien une erreur errConnexion
log.Fatal(err)
}
log.Fatal("Erreur inconnue")
}
}
On remarque l’utilisation de errors.Is() au lieu de ==, l’opérateur == renvoi false si on le compare à l’erreur.
Ajouter du texte en plus du message d’erreur
En Go, une erreur doit être traitée une seule fois. Il est possible d’ajouter des informations sur une erreur en utilisation la fonction Errorf() du package fmt.
Voici un exemple:
packagemainimport (
"fmt""log""os")
var (
fileOpenErrorstring = "Impossible d'ouvrir le fichier")
funcloadFile(fileNamestring) error {
_, err:=os.Open(fileName)
iferr!=nil {
returnfmt.Errorf("%s %w", fileOpenError, err) // "wrap" les messages d'erreur: on ajoute un message au message initial
}
returnnil}
funcmain() {
err:=loadFile("fichierQuiExistePas.txt")
iferr!=nil {
log.Fatal(err)
}
}
Créer une structure pour les erreurs
Il est possible de réimplémenter le type error pour permettre ajouter des informations à une erreur, par exemple il est possible d’ajouter un code d’erreur à une erreur.
Voici un exemple:
packagemainimport (
"fmt""log")
typeconnexionErrorstruct {
messagestringoriginalErrorerror// ici il est possible d'ajouter d'autres champs comme un code d'erreur
}
func (econnexionError) Error() string {
returnfmt.Sprintf("%s", e.message) // retourne notre message d'erreur
}
func (econnexionError) Unwrap() error {
returne.originalError// si il y a une erreur qui proviens d'un package de Go, on peux l'ajouter à notre structure personnalisée
}
funcaccountConnection(usernamestring, passwordstring) *connexionError {
varvalidUsernamestring = "name"varvaildPasswordstring = "pwd"ifusername!=validUsername {
return&connexionError{message: "Nom d'utilisateur invalide", originalError: nil}
}
ifpassword!=vaildPassword {
return&connexionError{message: "Mot de passe invalide", originalError: nil}
}
returnnil}
funcmain() {
err:=accountConnection("name", "pawd")
iferr!=nil {
log.Fatal("Erreur de connexion: ", err.message)
}
log.Println("Connexion réussie")
}
Combiner les sentinelles et les structures d’erreurs
Je ne montre que l’exemple, il parle de lui même.
packagemainimport (
"fmt""log")
var (
errBadLoginconnexionError = connexionError{message: "Nom d'utilisateur invalide", originalError: nil}
errBadPasswordconnexionError = connexionError{message: "Mot de passe invalide", originalError: nil}
)
typeconnexionErrorstruct {
messagestringoriginalErrorerror// ici il est possible d'ajouter d'autres champs comme un code d'erreur
}
func (econnexionError) Error() string {
returnfmt.Sprintf("%s", e.message) // retourne notre message d'erreur
}
funcaccountConnection(usernamestring, passwordstring) *connexionError {
varvalidUsernamestring = "name"varvaildPasswordstring = "pwd"ifusername!=validUsername {
return&errBadLogin }
ifpassword!=vaildPassword {
return&errBadPassword }
returnnil}
funcmain() {
err:=accountConnection("name", "pawd")
iferr!=nil {
log.Fatal("Erreur de connexion: ", err.message)
}
log.Println("Connexion réussie")
}