Catégories
Programmation

Les enums en Go, de zéro à héros

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 ?

Liens

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée.