Présent dans beaucoup de langages, les énumérations, ou enums en anglais, ne sont pourtant pas un type de base du langage Go. Les concepteurs avares en mot clés (grand bien leur fasse) ont pourtant laisser une piste et les bonnes pratiques ont émergés. Faisons ensemble le tour du propriétaire avec un exemple d’enum des jours de la semaine.
Implémentation naïve
Si vous n’avez aucune idée de comment implémenter les enums en Go, vous allez probablement arriver à quelque chose comme ça.
package GoEnumZeroHeros const Lundi int = 0 const Mardi int = 1 const Mercredi int = 2 const Jeudi int = 3 const Vendredi int = 4 const Samedi int = 5 const Dimanche int = 6
Amélioration syntaxique
En Go, il est recommandé de grouper les déclarations de variables ou de constantes et d’aligner les déclarations, nous allons donc passer à ça.
package GoEnumZeroHeros const ( Lundi int = 0 Mardi int = 1 Mercredi int = 2 Jeudi int = 3 Vendredi int = 4 Samedi int = 5 Dimanche int = 6 )
Déclaration de type
Go est un langage fortement typé, nous allons donc utiliser un alias pour marquer notre type. Créons un type Jour alias de int. C’est toujours mieux d’utiliser un type spécialisé pour profiter de la vérification des types à la compilation (et pendant l’écriture du code dans les IDEs). Et également pour ajouter des méthodes sur le type.
package GoEnumZeroHeros type Jour int const ( Lundi Jour = 0 Mardi Jour = 1 Mercredi Jour = 2 Jeudi Jour = 3 Vendredi Jour = 4 Samedi Jour = 5 Dimanche Jour = 6 )
Le mot clé iota
Je vous disais en intro que les concepteurs du langage avaient été très restrictif dans les mot clés mais ils ont pourtant introduit le mot clé iota. Celui-ci sert de raccourci syntaxique dans la déclaration de constantes numérique avec des valeurs qui augmentent.
iota va prendre la valeur 0 puis 1 puis 2, etc.
package GoEnumZeroHeros type Jour int const ( Lundi Jour = iota Mardi Mercredi Jeudi Vendredi Samedi Dimanche )
Il est possible de créer des valeurs complexes avec iota, de passer certaines valeurs, de repartir à zéro, etc., je vous laisse regarder la documentation officielle.
Optimisation du type
On sait que notre enum va commencer à 0 avec iota. On sait que notre scope est petit puisqu’on vise les jours de la semaine (7 pour l’instant…). Nous pouvons donc utiliser un entier non-signé et une taille de 8 bits.
package GoEnumZeroHeros type Jour uint8 const ( Lundi Jour = iota Mardi Mercredi Jeudi Vendredi Samedi Dimanche )
Astuce pour la limite basse
Si après une conversion (marshalling) en JSON on avait utilisé un champ en tant que Jour et que celui-ci était vide, avec un type Jour alias de int le champ va prendre la valeur par défaut, donc 0. Et dans notre enum 0, c’est lundi. Ouch, on ne veut pas que tous les champs vides de type Jour soit des lundis ! À la limite des dimanches, bon, mais des lundis, non… Pour cela, nous allons ajouter une valeur pour le cas inconnu et elle sera en première position.
package GoEnumZeroHeros type Jour uint8 const ( Inconnu Jour = iota Lundi Mardi Mercredi Jeudi Vendredi Samedi Dimanche )
Cela nous permet de vérifier que le jour est non présent ou avec une valeur correcte. Les valeurs vides ne sont plus des lundis mais ont une valeur à part.
Astuce pour la limite haute
Comment vérifier maintenant que les valeurs ne dépasse pas la dernière valeur ? La réponse est dans la question, avec une valeur de fin.
Nous allons implémenter une valeur qui sera connue comme valeur à ne pas égaler ou dépasser. Avec une valeur fixe, si on ajoute un jour dans l’enum, le code ne changera pas, on comparera toujours à cette valeur de fin.
package GoEnumZeroHeros type Jour uint8 const ( Inconnu Jour = iota Lundi Mardi Mercredi Jeudi Vendredi Samedi Dimanche fin )
Avez-vous remarqué que la dernière valeur ne commence pas par une majuscule ? Cela signifie en Go que cette valeur n’est pas exportée et utilisable hors du package. Cela permet de garder le code de vérification sur le type et que fin ne soit pas réutilisé ailleurs. Les utilisateurs du package n’ont pas à connaître cette valeur et doivent utiliser le code du type. Cela peut donner :
package GoEnumZeroHeros type Jour uint8 const ( Inconnu Jour = iota Lundi Mardi Mercredi Jeudi Vendredi Samedi Dimanche fin ) func (j *Jour) estUnJourValide() bool { return *j > Inconnu && *j < fin }
N’hésitez pas à créer des enums dans vos programmes même si cette structure n’est pas « de base » ou mise en avant dans le langage avec un mot clé particulier (même si iota est fait pour ça), cela permet de restreindre vos domaines et c’est toujours une bonne idée !
Une autre suggestion ?