En Go, les structures (struct) sont par défaut transmises et retournées par valeur. Cela signifie qu'à l'appel d'une fonction ou au retour de celle-ci, la structure entière est copiée. Pour les petites structures, cela est transparent, mais pour les grandes, la question est critique.
À l'origine, Go était orienté vers un travail efficace avec un faible nombre d'allocations. Cependant, le danger d'une copie involontaire de grandes données est apparu lorsque les structures utilisent de nombreux champs et objets imbriqués. Les performances de telles opérations peuvent en pâtir, et parfois la différence ne se révèle qu'au cours du profilage ou en raison de la gêne du GC.
Si une structure a une grande taille, sa copie à chaque appel de fonction, retour ou affectation s'avère coûteuse. Cela conduit à :
Pour les grandes structures, il est recommandé de passer et de retourner un pointeur vers la structure (*T), et non l'objet lui-même. Cela réduit les frais généraux et assure le travail avec une seule instance de données.
Exemple de code :
package main import "fmt" type Large struct { Data [1024]int } // Passage par valeur (incorrect pour les grands objets) func ValueProcess(l Large) { l.Data[0] = 123 // ne modifie que la copie } // Passage par pointeur func PointerProcess(l *Large) { l.Data[0] = 456 // modifie l'original } func main() { a := Large{} ValueProcess(a) fmt.Println("Après ValueProcess:", a.Data[0]) // 0 PointerProcess(&a) fmt.Println("Après PointerProcess:", a.Data[0]) // 456 }
Caractéristiques clés :
1. Peut-on retourner un pointeur sur une variable locale de structure depuis une fonction en Go ?
Oui. Go garantit la validité de tels pointeurs, en déplaçant automatiquement dans le tas les valeurs pointées (escape to heap).
func NewLarge() *Large { l := Large{} return &l }
2. L'original changera-t-il si on passe une structure par valeur à une fonction et qu'on modifie les champs à l'intérieur ?
Non : seule la copie changera, l'original restera le même en dehors de la fonction.
3. Faut-il toujours utiliser des pointeurs pour les structures ?
Non. Pour les petites structures (quelques champs), le passage par valeur est sûr et souvent préférable (immutable/value-semantic), économisant sur les allocations et réduisant la charge sur le GC.
Dans un service de journalisation, chaque événement représentait une grande structure et était retourné par les fonctions par valeur — chaque changement copiait entièrement la structure.
Avantages :
Inconvénients :
Nous avons opté pour le passage et le retour de structures par pointeur, en modifiant les données via des signatures comme func(l *Large) et func() *Large.
Avantages :
Inconvénients :