Menu

Structs en Golang : définir, initialiser, comparer et taguer

Les structs regroupent des champs nommés dans un seul type. Apprenez à les définir et à les initialiser, les valeurs zéro, les pointeurs vers des structs, les structs anonymes, la comparaison, les struct tags et la convention du constructeur NewX.

Cette page contient des éditeurs exécutables - modifiez, exécutez et voyez la sortie instantanément.

Définir et créer une struct

Un type struct est une liste de champs nommés. Vous le déclarez avec type Name struct { ... } et créez des valeurs avec un littéral composite.

Sortie :

Ana 31
{Name:Ana Email:ana@example.com Age:32 Admin:false}
{Name: Email: Age:0 Admin:false}
main.User{Name:"Bob", Email:"", Age:0, Admin:false}

%+v affiche les noms des champs et %#v la syntaxe Go. Les deux sont le moyen le plus rapide d'inspecter une struct pendant le débogage.

Des champs du même type peuvent partager une ligne : X, Y float64. Les noms de champs en majuscule sont exportés (visibles par les autres packages et par encoding/json) ; ceux en minuscule ne le sont pas.

Formes de littéraux

User{Name: "Ana", Age: 31}        // named fields: order-free, omitted fields are zero
User{"Ana", "a@x.com", 31, false} // positional: every field, in order
&User{Name: "Ana"}                // pointer to a new struct
new(User)                         // pointer to a zeroed struct

Préférez les champs nommés. Les littéraux positionnels cassent quand quelqu'un ajoute ou réordonne un champ, et go vet avertit à leur sujet pour les types struct d'autres packages (par exemple net/url.Error struct literal uses unkeyed fields). La forme positionnelle convient pour de tout petits types locaux comme Point{1, 2}.

Pas de valeurs par défaut : valeurs zéro et constructeurs

Go n'a ni valeurs par défaut de champs ni constructeurs dans le langage. Chaque champ démarre à la valeur zéro de son type : 0, "", false, nil. Deux idiomes comblent le manque.

Rendez la valeur zéro utile. sync.Mutex, bytes.Buffer et strings.Builder fonctionnent tous sans initialisation. Quand vous pouvez concevoir un type ainsi, faites-le.

Écrivez une fonction NewX quand un champ a besoin d'une valeur par défaut non nulle ou que quelque chose doit être alloué, comme une map :

NewServer renvoie un pointeur parce que le serveur a des méthodes qui le modifient et qu'il sera partagé. Renvoyer la struct par valeur est aussi courant pour les petits types immuables (time.Date renvoie un time.Time).

Pointeurs vers des structs

Go déréférence automatiquement les pointeurs de struct pour l'accès aux champs. Avec p := &User{}, vous écrivez p.Name, pas (*p).Name.

Sortie :

{11 12}
{11 12} {0 12}
{11 0}

Les structs sont des valeurs. L'affectation et les arguments de fonction les copient ; un pointeur en partage une. Les copies champ par champ sont superficielles : un champ slice ou map dans la copie pointe toujours vers les mêmes données. Voir les pointeurs pour savoir quand utiliser l'un ou l'autre.

Structs anonymes

Un type struct peut s'écrire en ligne sans nom. C'est utile pour des regroupements ponctuels, des tests pilotés par tableau et le décodage de JSON qu'on ne lit qu'une fois.

Le tableau []struct{ in string; want int } est la forme standard d'un test Go.

Comparer des structs

Les structs sont comparables avec == quand tous leurs champs sont comparables. L'égalité signifie que tous les champs sont égaux. Les structs comparables peuvent aussi servir de clés de map.

Pour une struct avec des champs slice ou map, écrivez une méthode Equal ou comparez dans les tests avec reflect.DeepEqual.

Struct tags

Un tag est un littéral de chaîne placé après le type d'un champ. Le langage l'ignore ; les bibliothèques le lisent par réflexion. Le lecteur le plus courant est encoding/json :

Ce code affiche {"id":7,"name":"Mug"} : le tag renomme les champs, omitempty retire le Price à zéro, et le champ non exporté est invisible pour l'encodeur. Les tags suivent la convention key:"value", avec plusieurs clés séparées par des espaces : `json:"name" db:"user_name"`. Un tag mal formé compile sans problème et est ignoré sans bruit, ce que go vet détecte. La page sur le json couvre les options.

Structs vides et ordre des champs

struct{} n'a aucun champ et occupe zéro octet. On l'utilise comme valeur d'ensemble (map[string]struct{}) et comme signal sur les channels (chan struct{}), où seul l'événement compte.

L'ordre des champs influe sur la disposition en mémoire : le compilateur ajoute du remplissage pour aligner les champs. Une struct bool, int64, bool occupe 24 octets sur les systèmes 64 bits, alors que int64, bool, bool en occupe 16. Cela ne compte que pour les types stockés en grand nombre.

Erreurs courantes

  • Modifier une copie. for _, u := range users { u.Age++ } modifie une copie à chaque fois. Utilisez l'indice : users[i].Age++.
  • Oublier d'initialiser un champ map. La valeur zéro de la struct contient une map nil ; y écrire provoque un panic. Allouez-la dans un constructeur.
  • Copier une struct qui contient un sync.Mutex. La copie a son propre état de verrou. Passez ces structs par pointeur ; go vet signale la copie.
  • Des littéraux positionnels pour les types d'autres packages. Ils cassent quand un champ est ajouté.

Questions fréquentes

Comment initialiser une struct en Go ?

Avec un littéral composite. Les champs nommés sont la forme habituelle : u := User{Name: "Ana", Age: 31}, et tout champ omis reçoit sa valeur zéro. User{} donne toutes les valeurs zéro, &User{...} donne un pointeur, et new(User) donne un pointeur vers une struct mise à zéro.

Go permet-il des valeurs par défaut pour les champs de struct ?

Non. Chaque champ démarre à la valeur zéro de son type. L'idiome est une fonction constructeur, func NewServer() *Server { return &Server{Port: 8080} }, ou bien concevoir le type pour que sa valeur zéro soit déjà utile, comme sync.Mutex et bytes.Buffer.

Comment comparer deux structs en Go ?

Avec ==, si tous les champs sont comparables. Deux valeurs struct sont égales quand tous les champs correspondants sont égaux. Une struct qui contient un champ slice, map ou fonction ne peut pas être comparée avec == (erreur de compilation) ; comparez les champs vous-même ou utilisez reflect.DeepEqual dans les tests.

Que sont les struct tags en Go ?

Des littéraux de chaîne placés après le type d'un champ, comme `json:"name,omitempty"`. Le compilateur les ignore ; des packages comme encoding/json, les drivers de base de données et les validateurs les lisent par réflexion pour contrôler la façon dont les champs sont encodés, nommés ou vérifiés.

Comment afficher une struct avec les noms des champs en Go ?

Utilisez fmt.Printf("%+v\n", s), qui affiche {Name:Ana Age:31}. %v n'affiche que les valeurs, et %#v affiche la syntaxe Go avec le nom du type : main.User{Name:"Ana", Age:31}.

Coddy programming languages illustration

Apprendre à coder avec Coddy

COMMENCER