Parenthèses étranges avant le nom de la fonction en Go : analyse approfondie
Imaginez : vous ouvrez du code Go écrit par quelqu'un d'autre, et vous voyez quelque chose qui vous fait tiquer :
func (u User) Greet() string { return "Bonjour, " + u.Name}Stop. Qu'est-ce que ce (u User) avant le nom de la fonction ? En Python, pour les méthodes, on utilise def greet(self):, en Java — public String greet() à l'intérieur d'une classe. Et là — des parenthèses, avec un type à l'intérieur. Absurde ? Non, génial !
Cette construction est l'une des fonctionnalités les plus puissantes de Go, qui déroute souvent les débutants. En réalité, il s'agit d'un receiver (récepteur) — la clé pour comprendre la programmation orientée objet en Go.
Dans cet article, nous allons jeter un coup d'œil sous le capot du compilateur Go, analyser comment fonctionnent ces parenthèses, et pourquoi sans elles votre code ressemblerait à celui des années 90. Prêt ? C'est parti !
Qu'est-ce qu'un receiver et à quoi sert-il ?
Go n'a pas de classes au sens classique du terme. Mais il a des structures (struct) et des méthodes. Les parenthèses avant le nom de la fonction sont précisément la déclaration d'une méthode pour un type spécifique. Techniquement, cela s'appelle un receiver parameter.
La syntaxe est simple :
func (receiver_name ReceiverType) MethodName(params) return_type { // corps de la méthode}Ici, receiver_name est une variable par laquelle vous accédez aux champs de la structure (comme self en Python ou this en Java). Et ReceiverType est votre type (généralement une structure).
Exemple concret :
package main
import "fmt"
type User struct { Name string Age int}
// Méthode avec receiverfunc (u User) SayHello() { fmt.Printf("Bonjour ! Je suis %s, j'ai %d ans.", u.Name, u.Age)}
func main() { user := User{Name: "Alice", Age: 25} user.SayHello() // Affiche : Bonjour ! Je suis Alice, j'ai 25 ans.}Remarquez : nous appelons la méthode avec un point, comme dans tout langage POO. Mais sous le capot, Go fait une astuce ingénieuse.
Value receiver vs Pointer receiver : le choc des titans
En Go, il existe deux types de receivers : par valeur (value receiver) et par pointeur (pointer receiver). La différence est colossale, et ne pas la comprendre est une cause fréquente de bugs.
Value receiver
Quand vous écrivez func (u User) ..., Go copie toute la structure. La méthode travaille sur une copie de l'objet. Les modifications à l'intérieur de la méthode n'affecteront pas l'original.
func (u User) Birthday() { u.Age++ // Cela ne modifie que la copie !}
func main() { user := User{Name: "Bob", Age: 30} user.Birthday() fmt.Println(user.Age) // Toujours 30 ! Bug ?}Ce code ne générera pas d'erreur de compilation, mais il est logiquement incorrect. Vous vous attendez à ce que l'âge augmente, mais il reste le même. Bienvenue dans le monde du value receiver !
Pointer receiver
Si vous utilisez un pointeur : func (u *User) ..., la méthode reçoit une référence à l'objet original. Les modifications seront alors visibles par tous.
func (u *User) Birthday() { u.Age++ // Nous modifions l'original}
func main() { user := &User{Name: "Bob", Age: 30} // Création d'un pointeur user.Birthday() fmt.Println(user.Age) // 31 ! Ça marche !}Règle empirique : si la méthode modifie l'état de la structure ou si la structure est grande (pour éviter la copie), utilisez un pointer receiver. Si la méthode ne fait que lire les données — utilisez un value receiver.
Comment cela fonctionne-t-il sous le capot ?
Voici la partie la plus intéressante. Lorsque le compilateur Go voit :
type Rectangle struct { Width, Height float64}
func (r Rectangle) Area() float64 { return r.Width * r.Height}Il transforme cela en une fonction ordinaire, où le receiver devient le premier argument :
Blogs
Book Recommendations
Lire aussi
Vérification de sous-chaîne en Swift : guide complet avec exemples de code
AsyncTask est mort dans Android 11 : 3 alternatives et erreurs typiques
Déploiement d'une application JavaScript sur VPS : guide étape par étape
TypeScript ne voit pas les SVG ? Résolvons le problème une fois pour toutes
Gestion de la mémoire en TypeScript : comment fonctionne le ramasse-miettes