Le mot-clé struct regroupe des champs nommés dans un seul type. En Go, c'est avec une déclaration type Order struct { ... } que vous décrivez une commande, un utilisateur, le corps d'une requête HTTP ou un fichier de configuration. Les structs n'ont ni constructeurs ni héritage. Vous ajoutez du comportement avec des méthodes, réutilisez du code en intégrant un struct dans un autre et contrôlez l'encodage avec des struct tags (spécification Go).
En bref
type Order struct { ... }déclare un type struct. Chaque champ a un nom et un type, et les champs de même type peuvent partager une ligne.- Dans la valeur zéro d'un struct, chaque champ vaut sa propre valeur zéro, donc
var o Orderest utilisable sans aucune initialisation. - Écrivez les littéraux composites avec les noms de champs, comme
Order{ID: 1001, Total: 4999}.go vetsignale les littéraux sans noms de champs pour les types d'autres packages. - Seuls les champs exportés (avec une majuscule) sont visibles par
encoding/jsonet les autres packages. Un champ en minuscules est ignoré sans erreur. - Les struct tags comme
json:"email,omitempty"contrôlent l'encodage. Utilisezomitzero(Go 1.24) pourtime.Timeet les autres structs, caromitemptyne les omet jamais. - Les méthodes à récepteur pointeur peuvent modifier le struct. Les méthodes à récepteur valeur travaillent sur une copie.
- Intégrer un type promeut ses champs et ses méthodes vers le struct englobant. C'est ainsi que Go réutilise du code sans héritage.
- Les structs ne sont comparables avec
==que si tous leurs champs le sont. Affecter un struct le copie, mais les slices et les maps qu'il contient restent partagées.
Comment déclarer un struct en Go ?
Écrivez type, le nom, struct puis les champs entre accolades. Chaque champ est un nom suivi d'un type :
example.gogopackage main import ( "fmt" "time" ) type Order struct { ID int64 CustomerID int64 Status string Total int64 // cents CreatedAt time.Time } type Address struct { Street, City, Country string } func main() { var o Order fmt.Printf("%+v\n", o) fmt.Println(o.Status == "", o.CreatedAt.IsZero()) }
example.texttext{ID:0 CustomerID:0 Status: Total:0 CreatedAt:0001-01-01 00:00:00 +0000 UTC} true true
Address montre la forme courte. Les champs de même type peuvent partager une ligne, séparés par des virgules. La plupart des codebases gardent malgré tout un champ par ligne, car les diffs restent petits et il reste de la place pour un tag ou un commentaire.
var o Order ne demande aucun constructeur. Chaque champ démarre à sa valeur zéro : 0 pour les nombres, "" pour les chaînes, nil pour les pointeurs, les slices et les maps, et le time.Time zéro pour CreatedAt. Le mot-clé var en Go traite les valeurs zéro en détail.
Comme tout type, un struct peut être déclaré dans une fonction quand seule cette fonction en a besoin. Le mot-clé type en Go montre un struct local loginRequest utilisé pour décoder un corps JSON.
Comment créer une valeur de struct ?
Vous utiliserez surtout la valeur zéro obtenue avec var, un littéral composite avec noms de champs, ou un pointeur vers un littéral avec & :
example.gogokeyed := Order{ID: 1001, CustomerID: 42, Status: "paid", Total: 4999} partial := Order{ID: 1002, Status: "pending"} positional := Order{1003, 42, "paid", 1250} p := &Order{ID: 1004} p.Status = "shipped"
example.texttext{ID:1001 CustomerID:42 Status:paid Total:4999} {ID:1002 CustomerID:0 Status:pending Total:0} {ID:1003 CustomerID:42 Status:paid Total:1250} {ID:1004 CustomerID:0 Status:shipped Total:0}
Un littéral avec noms de champs peut lister n'importe quel sous-ensemble de champs dans n'importe quel ordre, et les autres prennent leur valeur zéro. Un littéral positionnel doit lister tous les champs dans l'ordre de déclaration, ce qui le rend fragile. Ajoutez un champ Currency à Order et tous les littéraux positionnels cessent de compiler, alors que ceux avec noms de champs continuent de fonctionner.
Pour les types struct d'autres packages, go vet signale les littéraux positionnels via son analyseur composites :
example.gogoaddr := net.TCPAddr{net.ParseIP("10.0.0.5"), 5432, ""}
example.texttextmain.go:9:10: net.TCPAddr struct literal uses unkeyed fields
p.Status fonctionne sur un pointeur sans écrire (*p).Status. Go déréférence automatiquement un pointeur vers un struct quand vous accédez à un champ.
Comment renseigner un champ pointeur optionnel en une ligne ?
Utilisez new avec une valeur, ce qui fonctionne depuis Go 1.26. Les champs pointeurs sont la façon habituelle de distinguer « non envoyé » de « envoyé à zéro » dans le corps d'une requête PATCH, et avant Go 1.26 il fallait une variable temporaire ou une fonction utilitaire pour chacun d'eux :
example.gogotype UpdateUser struct { Name *string `json:"name,omitempty"` Email *string `json:"email,omitempty"` Age *int `json:"age,omitempty"` } func main() { patch := UpdateUser{ Email: new("[email protected]"), Age: new(31), } out, _ := json.Marshal(patch) fmt.Println(string(out)) }
example.texttext{"email":"[email protected]","age":31}
new(31) alloue un int, y stocke 31 et renvoie le pointeur (notes de version de Go 1.26). Avec go 1.25 dans go.mod, le compilateur le refuse :
example.texttext./main.go:16:10: new("[email protected]") requires go1.26 or later (-lang was set to go1.25; check go.mod)
Pourquoi les champs en minuscules d'un struct manquent-ils dans le JSON ?
Parce qu'ils ne sont pas exportés, et que les autres packages ne voient pas les champs non exportés. encoding/json est un autre package, il les ignore donc sans erreur :
example.gogotype User struct { ID int64 Email string password string role string } func main() { u := User{ID: 7, Email: "[email protected]", password: "hunter2", role: "admin"} out, err := json.Marshal(u) fmt.Println(string(out), err) var in User err = json.Unmarshal([]byte(`{"ID":8,"Email":"[email protected]","role":"admin"}`), &in) fmt.Printf("%+v %v\n", in, err) }
example.texttext{"ID":7,"Email":"[email protected]"} <nil> {ID:8 Email:[email protected] password: role:} <nil>
Les deux appels renvoient une erreur nil, et le role présent en entrée disparaît sans le moindre message. Quand une API Go renvoie {} ou qu'un champ reste vide après le décodage, vérifiez ce point en premier. La correction consiste à mettre une majuscule au champ et à utiliser un tag pour conserver le nom JSON en minuscules.
La même règle s'applique à tous les packages qui lisent les champs par réflexion : encoding/xml, les scanners de database/sql, GORM, les bibliothèques YAML et l'exécution des templates. Vous pouvez aussi en tirer parti volontairement. Un champ non exporté est un endroit sûr pour des données qui ne doivent jamais être encodées, comme un handle de base de données ou un cache.
Que sont les struct tags en Go ?
Un struct tag est une chaîne littérale placée après le type d'un champ. Go lui-même l'ignore, et les packages le lisent par réflexion pour décider comment traiter ce champ. Le tag JSON est celui que vous écrirez le plus souvent :
example.gogotype User struct { ID int64 `json:"id"` Email string `json:"email"` Nickname string `json:"nickname,omitempty"` PasswordHash string `json:"-"` DeletedAt time.Time `json:"deleted_at,omitempty"` LastLoginAt time.Time `json:"last_login_at,omitzero"` } func main() { u := User{ID: 7, Email: "[email protected]", PasswordHash: "$2a$10$..."} enc := json.NewEncoder(os.Stdout) enc.SetIndent("", " ") enc.Encode(u) }
example.texttext{ "id": 7, "email": "[email protected]", "deleted_at": "0001-01-01T00:00:00Z" }
Reprenons la sortie champ par champ :
json:"id"renomme le champ dans la sortie.omitemptysupprime le champ quand il vautfalse,0,"",nilou une slice ou une map vide.Nicknameest vide, il disparaît donc.json:"-"n'encode jamais le champ.PasswordHashreste absent de toutes les réponses.omitemptyne fonctionne pas sur les structs.DeletedAtest untime.Timezéro, et il apparaît quand même sous la forme0001-01-01T00:00:00Z, que les clients ont tendance à interpréter comme une vraie date.omitzero, ajouté dans Go 1.24, supprime un champ quand il contient la valeur zéro de son type. Pour les types dotés d'une méthodeIsZero() bool, commetime.Time, il appelle cette méthode.LastLoginAta disparu (notes de version de Go 1.24).
Pour du nouveau code, utilisez omitzero sur les champs de type struct et time.Time. omitempty reste adapté aux chaînes, aux nombres, aux slices et aux maps.
Un champ peut porter des tags pour plusieurs packages, séparés par des espaces : `json:"email" db:"email" validate:"required,email"`. Chaque package ne lit que sa propre clé avec reflect.StructTag.Get :
example.gogof, _ := reflect.TypeFor[User]().FieldByName("Nickname") fmt.Println(f.Tag.Get("json"))
example.texttextnickname,omitempty
Pourquoi mon struct tag est-il ignoré ?
En général à cause d'un espace ou d'un guillemet manquant. Le format est strict : key:"value", sans espace après les deux-points. json: "email" semble correct à un humain, mais Tag.Get("json") renvoie une chaîne vide, et le champ est donc encodé sous le nom Email. Le compilateur accepte n'importe quelle chaîne comme tag, il ne dira donc rien. go vet, lui, le signale via son analyseur structtag, et il détecte aussi deux champs portant le même nom JSON :
example.gogotype User struct { Email string `json: "email"` Name string `json:"name" db:"name"` ID int64 `json:"id"` Ref int64 `json:"id"` }
example.texttextmain.go:4:2: struct field tag `json: "email"` not compatible with reflect.StructTag.Get: bad syntax for struct tag value main.go:7:2: struct field Ref repeats json tag "id" also at main.go:6
Les éditeurs qui exécutent gopls affichent les mêmes avertissements pendant la saisie.
Comment fonctionnent les méthodes sur les structs ?
Une méthode est une fonction dotée d'un récepteur, écrit avant le nom. Le récepteur est soit une valeur (c Cart), soit un pointeur (c *Cart), et ce choix détermine si la méthode peut modifier le struct :
example.gogotype Cart struct { Items []string Total int64 } func (c Cart) AddValue(item string, price int64) { c.Items = append(c.Items, item) c.Total += price } func (c *Cart) Add(item string, price int64) { c.Items = append(c.Items, item) c.Total += price } func main() { var c Cart c.AddValue("keyboard", 4999) fmt.Println(len(c.Items), c.Total) c.Add("keyboard", 4999) fmt.Println(len(c.Items), c.Total) }
example.texttext0 0 1 4999
AddValue compile et s'exécute, mais le panier reste vide. Un récepteur valeur est une copie du struct : la méthode met donc à jour la copie, puis la jette. Ce bug revient souvent dans du code Go écrit par des débutants, car aucune erreur ni aucun avertissement ne le signale. Add reçoit un pointeur et modifie le panier de l'appelant.
Vous n'avez pas besoin d'écrire (&c).Add(...). Quand c est adressable, Go prend l'adresse à votre place. L'inverse fonctionne aussi : un pointeur peut appeler des méthodes à récepteur valeur.
Utilisez un récepteur pointeur quand la méthode modifie le struct, quand le struct est volumineux ou quand il contient un sync.Mutex ou un champ similaire qui ne doit pas être copié. Utilisez un récepteur valeur pour de petites valeurs immuables comme un Money ou un Point. Si une méthode d'un type a besoin d'un récepteur pointeur, donnez un récepteur pointeur à toutes. Mélanger les deux rend l'ensemble de méthodes plus difficile à cerner, car seul *Cart possède alors toutes les méthodes, et seul *Cart satisfait les interfaces qui les exigent toutes (spécification Go).
Comment fonctionne l'embedding de structs en Go ?
Un champ écrit avec seulement un type, sans nom, est un champ intégré (embedded field). Ses champs et ses méthodes sont promus, ce qui signifie que vous pouvez les utiliser comme s'ils étaient déclarés sur le struct englobant :
example.gogotype Timestamps struct { CreatedAt time.Time UpdatedAt time.Time } func (t *Timestamps) Touch(now time.Time) { if t.CreatedAt.IsZero() { t.CreatedAt = now } t.UpdatedAt = now } type Order struct { ID int64 Timestamps } type User struct { ID int64 Email string Timestamps } func main() { now := time.Date(2026, 9, 26, 9, 0, 0, 0, time.UTC) var o Order o.Touch(now) fmt.Println(o.CreatedAt.Format(time.DateOnly), o.Timestamps.UpdatedAt.Format(time.Kitchen)) u := User{ID: 7, Email: "[email protected]", Timestamps: Timestamps{CreatedAt: now}} fmt.Println(u.CreatedAt.Year()) }
example.texttext2026-09-26 9:00AM 2026
o.Touch(now) est un raccourci pour o.Timestamps.Touch(now). Le champ intégré existe toujours sous le nom de son type, et c'est aussi ainsi que vous le renseignez dans un littéral : Timestamps: Timestamps{...}. encoding/json aplatit également les structs intégrés, donc l'Order ci-dessus est encodé en {"ID":0,"CreatedAt":"2026-09-26T09:00:00Z","UpdatedAt":"2026-09-26T09:00:00Z"}.
L'embedding n'est pas de l'héritage. Un Order n'est pas un Timestamps, et vous ne pouvez pas passer un Order à une fonction qui attend un Timestamps. Quand Touch s'exécute, son récepteur est le Timestamps interne. Il n'a aucun moyen d'atteindre l'Order qui l'entoure ni d'appeler des méthodes définies par Order. Une sous-classe en Java ou en Python le peut, et c'est de là que viennent les questions du type « quelle redéfinition s'exécute ? ». En Go, elles ne se posent pas.
Quand le struct englobant déclare un champ ou une méthode du même nom, c'est lui qui l'emporte, et l'élément intégré reste accessible via le nom du type :
example.gogotype Base struct{ ID int64 } func (Base) Describe() string { return "base" } type Order struct { Base ID string } func (Order) Describe() string { return "order" } func main() { o := Order{Base: Base{ID: 42}, ID: "ord_42"} fmt.Println(o.ID, o.Base.ID) fmt.Println(o.Describe(), o.Base.Describe()) }
example.texttextord_42 42 order base
Peut-on intégrer une interface dans un struct ?
Oui, et c'est une façon courante d'envelopper un type tout en redéfinissant une seule méthode. Un middleware HTTP qui enregistre le code de statut intègre http.ResponseWriter, obtient Header et Write sans rien écrire et ne remplace que WriteHeader :
example.gogotype statusRecorder struct { http.ResponseWriter status int } func (r *statusRecorder) WriteHeader(code int) { r.status = code r.ResponseWriter.WriteHeader(code) } func logStatus(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { rec := &statusRecorder{ResponseWriter: w, status: http.StatusOK} next.ServeHTTP(rec, req) slog.Info("request", "path", req.URL.Path, "status", rec.status) }) }
example.texttext2026/09/26 09:04:44 INFO request path=/orders/99 status=404
Comme *statusRecorder possède les trois méthodes, il satisfait http.ResponseWriter et peut être passé au handler suivant. Si l'interface intégrée vaut nil, appeler l'une de ses méthodes provoque un panic, donc renseignez-la toujours dans le littéral.
Faut-il intégrer sync.Mutex dans un struct ?
Pas dans un type exporté. L'embedding promeut Lock et Unlock dans l'API publique du type, et n'importe quel appelant peut alors verrouiller votre cache depuis l'extérieur :
example.gogotype Cache struct { sync.Mutex items map[string]string }
Un champ nommé et non exporté fait du verrou un détail d'implémentation :
example.gogotype SafeCache struct { mu sync.Mutex items map[string]string } func (c *SafeCache) Get(key string) (string, bool) { c.mu.Lock() defer c.mu.Unlock() v, ok := c.items[key] return v, ok }
La valeur zéro de sync.Mutex est un mutex déverrouillé, donc SafeCache n'a besoin d'aucune initialisation pour le verrou. Sa map doit quand même être créée avant la première écriture.
Peut-on comparer et copier des structs en Go ?
Vous pouvez comparer deux structs avec == quand tous leurs champs sont comparables. La comparaison se fait champ par champ. Les petits structs font ainsi de bons types valeur et de bonnes clés de map :
example.gogotype Money struct { Amount int64 Currency string } type RouteKey struct { Method string Path string } func main() { fmt.Println(Money{4999, "EUR"} == Money{4999, "EUR"}) hits := map[RouteKey]int{} hits[RouteKey{"GET", "/orders"}]++ hits[RouteKey{"GET", "/orders"}]++ hits[RouteKey{"POST", "/orders"}]++ fmt.Println(hits[RouteKey{"GET", "/orders"}], len(hits)) }
example.texttexttrue 2 2
Une clé composite comme RouteKey est plus propre que des chaînes collées sous la forme "GET /orders", et elle ne peut pas entrer en collision quand un chemin contient un espace. Les littéraux positionnels conviennent ici, car RouteKey est déclaré dans le même package et possède deux champs évidents.
Les slices, les maps et les fonctions ne sont pas comparables, donc un struct qui en contient ne l'est pas non plus. Le compilateur vous le signale :
example.gogotype Order struct { ID int64 Items []string } func main() { a := Order{ID: 1} b := Order{ID: 1} fmt.Println(a == b) }
example.texttext./main.go:13:14: invalid operation: a == b (struct containing []string cannot be compared)
Comparez ces structs champ par champ, avec slices.Equal pour la slice, ou avec reflect.DeepEqual dans les tests.
L'affectation d'un struct le copie-t-elle ?
Oui, mais la copie est superficielle. Chaque champ est copié, et un champ slice ou map est copié sous la forme d'un en-tête qui pointe toujours vers les mêmes données :
example.gogotype Order struct { ID int64 Items []string Meta map[string]string } func main() { original := Order{ID: 1, Items: []string{"keyboard"}, Meta: map[string]string{"source": "web"}} copied := original copied.ID = 2 copied.Items[0] = "mouse" copied.Meta["source"] = "api" fmt.Println(original.ID, original.Items, original.Meta) }
example.texttext1 [mouse] map[source:api]
Modifier copied.ID n'a pas touché l'original. Modifier un élément de copied.Items ou une clé de copied.Meta a aussi modifié l'original. Pour obtenir une copie indépendante, clonez vous-même ces champs avec slices.Clone et maps.Clone.
La même copie a lieu quand vous passez un struct par valeur à une fonction. Pour un struct qui contient un sync.Mutex, cette copie est un bug, car la copie obtient son propre verrou. go vet le signale via l'analyseur copylocks :
example.gogotype Counter struct { mu sync.Mutex hits int } func report(c Counter) { fmt.Println(c.hits) }
example.texttextmain.go:13:15: report passes lock by value: shop.Counter contains sync.Mutex main.go:19:9: call of report copies lock value: shop.Counter contains sync.Mutex
La correction est func report(c *Counter).
Qu'est-ce qu'un struct anonyme en Go ?
C'est un type struct écrit en ligne, sans nom. Vous l'utilisez quand une forme n'est nécessaire qu'une seule fois. Les deux endroits où vous le verrez le plus sont les tests pilotés par table et le décodage d'une partie d'une réponse JSON :
example.gogovar resp struct { Data struct { Status string `json:"status"` } `json:"data"` } body := `{"data":{"status":"shipped","carrier":"DHL"},"meta":{"request_id":"abc"}}` if err := json.Unmarshal([]byte(body), &resp); err != nil { panic(err) } fmt.Println(resp.Data.Status) tests := []struct { name string input string want string }{ {"trims spaces", " [email protected] ", "[email protected]"}, {"already clean", "[email protected]", "[email protected]"}, } for _, tt := range tests { got := strings.ToLower(strings.TrimSpace(tt.input)) fmt.Println(tt.name, got == tt.want) }
example.texttextshipped trims spaces true already clean true
Le décodeur ne remplit que les champs déclarés par le struct, donc carrier et meta sont ignorés. Vous n'avez pas besoin d'un type nommé pour une réponse dont vous ne lisez qu'un champ. Dès que la même forme apparaît à deux endroits, donnez-lui un nom.
À quoi sert struct{} ?
struct{} est un struct sans aucun champ. Sa taille est de zéro octet, c'est donc le type valeur de prédilection quand seule la présence de quelque chose compte. Les deux cas courants sont un ensemble construit sur une map et un channel qui sert uniquement à signaler :
example.gogoseen := map[string]struct{}{} for _, id := range []string{"evt_1", "evt_2", "evt_1"} { if _, dup := seen[id]; dup { fmt.Println("skipping duplicate", id) continue } seen[id] = struct{}{} } fmt.Println(len(seen), unsafe.Sizeof(struct{}{})) done := make(chan struct{}) go func() { defer close(done) }() <-done fmt.Println("worker finished")
example.texttextskipping duplicate evt_1 2 0 worker finished
map[string]struct{} exprime « ensemble de chaînes » plus clairement que map[string]bool, où le lecteur doit se demander ce que signifie false. chan struct{} indique qu'aucune donnée ne transite sur le channel, seulement le fait qu'il a été fermé. ctx.Done() renvoie un <-chan struct{} pour la même raison.
L'ordre des champs modifie-t-il la taille d'un struct ?
Oui. Chaque champ est aligné sur sa taille, donc le compilateur insère du padding entre un petit champ et un champ plus grand qui le suit. Sur une machine 64 bits, les cinq mêmes champs occupent deux fois plus de mémoire dans le mauvais ordre :
example.gogotype EventLoose struct { Active bool ID int64 Retried bool Attempts int32 Urgent bool } type EventPacked struct { ID int64 Attempts int32 Active bool Retried bool Urgent bool } func main() { fmt.Println(unsafe.Sizeof(EventLoose{}), unsafe.Sizeof(EventPacked{})) }
example.texttext32 16
EventLoose ajoute 7 octets de padding après Active pour que ID commence sur une frontière de 8 octets, puis encore après Retried et Urgent. EventPacked ordonne les champs du plus grand au plus petit et ne perd qu'1 octet à la fin.
Pour la plupart des structs, cela n'a pas d'importance, et regrouper les champs par sens est plus lisible. Cela commence à compter quand vous gardez des millions de valeurs dans une slice ou un cache. L'analyseur fieldalignment de golang.org/x/tools signale les structs qui pourraient être plus petits.
Struct Go vs classe : quelle différence ?
Un struct Go contient des données et peut avoir des méthodes, ce qui couvre l'essentiel de ce que fait une classe en Java, C# ou Python. Le reste diffère volontairement :
| Struct Go | Classe (Java, C#, Python) | |
|---|---|---|
| Constructeur | Aucun. Valeur zéro, ou une fonction NewX par convention | Méthode constructeur dédiée |
| Réutilisation | Embedding (composition) | Héritage, plus composition |
| Polymorphisme | Interfaces, satisfaites implicitement | Classes de base et interfaces déclarées |
| Visibilité | Par package, selon la majuscule initiale | public, private, protected par membre |
| Méthodes | Déclarées hors du type, n'importe où dans le package | Déclarées dans le corps de la classe |
| Sémantique de copie | Valeur par défaut, & pour un pointeur | Référence par défaut |
Quand un struct a besoin de validation ou de valeurs par défaut, écrivez une simple fonction qui le renvoie. La convention est NewX, et il est normal qu'elle renvoie aussi une erreur :
example.gogotype Client struct { baseURL string http *http.Client retries int } func NewClient(baseURL string) (*Client, error) { if baseURL == "" { return nil, errors.New("baseURL is required") } return &Client{ baseURL: baseURL, http: &http.Client{Timeout: 10 * time.Second}, retries: 3, }, nil }
Comme les champs ne sont pas exportés, les appelants extérieurs au package ne peuvent obtenir un Client que via NewClient, avec les valeurs par défaut appliquées. Go n'ayant ni super ni redéfinition, vous ne remontez jamais une hiérarchie de classes pour savoir ce que fait une méthode. Le comportement d'un struct se compose de ses propres méthodes et de celles qu'il intègre, et vous pouvez toutes les lire dans un seul package. Cela aide beaucoup quand une codebase Go grandit.
La place de LevelUpGo
LevelUpGo enseigne Go au travers d'exercices qui exécutent du vrai code Go dans le navigateur. Composite Types construit pas à pas votre premier struct, des fonctions constructeurs, des méthodes et l'embedding, puis les utilise dans un petit projet. Pointers & Memory couvre les pointeurs vers des structs, new et les récepteurs valeur ou pointeur, et se termine par un store de feature flags qui met à jour des structs en place. Interfaces & Polymorphism montre comment les structs satisfont des interfaces sans le déclarer. Le Training Ground propose de courts exercices indépendants pour pratiquer en dehors d'un cours. Pour les 24 autres mots réservés, consultez Les mots-clés Go : les 25 expliqués.
Questions fréquentes
struct est-il un mot-clé en Go ?
Oui. struct fait partie des 25 mots-clés réservés de Go, vous ne pouvez donc pas l'utiliser comme nom de variable ou de fonction. Il introduit un type struct, presque toujours nommé avec type, comme dans type Order struct { ... }. Il apparaît aussi dans les structs anonymes et dans le struct vide struct{}.
Go a-t-il des classes ?
Non. Go propose à la place des structs avec des méthodes, des interfaces et l'embedding. Un struct contient les données, les méthodes ajoutent du comportement, les interfaces fournissent le polymorphisme et l'embedding réutilise du code par composition. Il n'y a ni héritage ni mot-clé de constructeur. Par convention, une fonction NewX joue ce rôle.
Faut-il utiliser un récepteur valeur ou pointeur ?
Utilisez un récepteur pointeur si la méthode modifie le struct, si le struct est volumineux ou s'il contient un sync.Mutex. Utilisez un récepteur valeur pour de petites valeurs qui ne changent jamais, comme un Money ou un Point. Si une méthode d'un type a besoin d'un récepteur pointeur, donnez un récepteur pointeur à toutes pour que le type se comporte de façon cohérente.
Pourquoi json.Marshal renvoie-t-il un objet vide pour mon struct ?
Parce que les champs du struct ne sont pas exportés. encoding/json ne voit que les champs qui commencent par une majuscule, et il ignore les autres sans erreur. Renommez email en Email et ajoutez `json:"email"` pour conserver le nom en minuscules dans le JSON.
Quelle est la différence entre omitempty et omitzero ?
omitempty supprime false, 0, "", nil ainsi que les slices et les maps vides, mais ne supprime jamais un struct : un time.Time zéro apparaît donc toujours sous la forme 0001-01-01T00:00:00Z. omitzero, ajouté dans Go 1.24, supprime tout champ qui contient la valeur zéro de son type, et il utilise la méthode IsZero du type quand elle existe.
Sources
- The Go Programming Language Specification, Struct types : https://go.dev/ref/spec#Struct_types
- The Go Programming Language Specification, Composite literals : https://go.dev/ref/spec#Composite_literals
- The Go Programming Language Specification, Method sets : https://go.dev/ref/spec#Method_sets
- The Go Programming Language Specification, Comparison operators : https://go.dev/ref/spec#Comparison_operators
- encoding/json, Marshal : https://pkg.go.dev/encoding/json#Marshal
- reflect, StructTag : https://pkg.go.dev/reflect#StructTag
- Notes de version de Go 1.24, omitzero : https://go.dev/doc/go1.24
- Notes de version de Go 1.26, new avec une expression : https://go.dev/doc/go1.26
- Analyseurs de go vet (composites, structtag, copylocks) : https://pkg.go.dev/cmd/vet
