En Go, par défaut, tous les arguments de fonction sont passés par valeur : la valeur de la variable est copiée. Mais certains types (comme les tranches, les cartes, les canaux) sont des « enveloppes » autour de structures internes (pointeurs). Le passage d'une tranche par valeur ne copie que le descripteur de la tranche, et non les données ; les deux variables pointent vers le même tableau. Dans le cas des structures, la structure entière est copiée.
Si l'on souhaite éviter la copie et travailler avec la structure originale, on utilise le passage par pointeur (*Struct).
type User struct { Name string Age int } func updateUser(u User) { u.Age = 30 // n'affecte que la copie } func updateUserPtr(u *User) { u.Age = 30 // affecte l'original } func main() { u := User{"Ivan", 25} updateUser(u) fmt.Println(u.Age) // 25 updateUserPtr(&u) fmt.Println(u.Age) // 30 }
Les modifications apportées à une tranche passée à une fonction sont-elles toujours visibles à l'extérieur de la fonction ?
Non !
slice[i] = ...), cela est visible à l'extérieur.slice = append(slice, ...)), et que le résultat n'est pas renvoyé depuis la fonction — les nouveaux éléments seront uniquement dans la copie locale, et vous les perdrez.func addElem(s []int) { s = append(s, 100) } func main() { arr := []int{1,2,3} addElem(arr) fmt.Println(arr) // [1 2 3] — 100 n'a pas été ajouté }
Histoire
Dans l'un des projets, des structures de données avec un grand champ struct (200+ octets) étaient passées par valeur via des canaux entre goroutines, ce qui entraînait d'énormes frais de copie et des pertes de performance. Après être passés au passage par pointeur, la latence a été réduite d'un ordre de grandeur.
Histoire
Dans un service de journal d'audit, un développeur passait une carte (map) entre des fonctions sans clonage explicite (copy). Les modifications apportées par une fonction changeaient de manière inattendue les données dans une autre partie du programme, provoquant de la confusion dans le journal.
Histoire
Dans une fonction d'augmentation dynamique de la tranche, il a été oublié de renvoyer la nouvelle tranche. En conséquence, les modifications n’étaient pas reflétées dans le code appelant, ce qui a entraîné une perte de certaines transactions. Il a été décidé de renvoyer la nouvelle tranche depuis la fonction.