En Go, les méthodes peuvent être déclarées à la fois pour des valeurs et pour des pointeurs (value/pointer receiver). Cette fonctionnalité a été conservée depuis les premières versions du langage pour un contrôle explicite sur qui va modifier les données d'origine. Le problème classique est la nécessité de la distance entre la sémantique value (copie, ne modifie pas) et pointer (accès partagé aux données et possibilité de modification).
Problème — il est facile de se tromper en déclarant une méthode avec un value receiver et de ne pas obtenir l'effet attendu, ou d'appeler une méthode value sur une variable pointer.
Solution — respecter les règles suivantes :
Exemple de code :
type Counter struct { Value int } func (c Counter) IncCopy() { c.Value++ } // value receiver func (c *Counter) IncPointer() { c.Value++ } // pointer receiver c := Counter{} c.IncCopy() // Value restera 0 c.IncPointer() // Value deviendra 1
Caractéristiques clés :
Peut-on appeler une méthode avec value receiver sur un pointeur, et une méthode pointer sur une valeur ?
Go "sous le capot" déréférence automatiquement les pointeurs ou prend leur adresse, donc l'appel est autorisé si les types sont compatibles. Mais pas toujours — cela ne fonctionne pas de la même manière avec les interfaces.
var c Counter (&c).IncCopy() // Peut appeler la méthode value via le pointeur c.IncPointer() // Peut appeler la méthode pointer, Go prendra automatiquement l'adresse
Que se passe-t-il si une structure n'implémente que des méthodes pointer, mais qu'elle est transmise par valeur dans une interface ?
Un tel objet n'implémente pas l'interface si elle nécessite des méthodes pointer, donc un panic ou une erreur de compilation est possible.
type D interface { IncPointer() } func f(d D) {} c := Counter{} f(c) // erreur ! Counter par valeur n'implémente pas l'interface f(&c) // correct
La structure changera-t-elle lors de l'appel d'une méthode pointer receiver si une copie du pointeur est transmise ?
Oui, même si le pointeur est copié, l'objet sous-jacent reste le même — le résultat sera identique.
c := Counter{} p := &c p2 := p p2.IncPointer() // Value augmentera
Un ingénieur implémente une structure avec des méthodes "Update" de value receiver. La structure est transmise via une interface, mais les modifications "disparaissent" — car elles travaillent avec une copie.
Avantages :
Inconvénients :
Accord explicite dans l'équipe : toutes les méthodes modifiant l'état ne peuvent être que des pointer receivers, les interfaces ne sont mises en œuvre que par pointers, value — pour des "extensions" et des utilitaires.
Avantages :
Inconvénients :