Lezioni del modulo (2/5)
Metodi: receiver value vs pointer
Un metodo in Go è una funzione con un receiver: un parametro
speciale dichiarato fra func e il nome del metodo, che lega il metodo
a un tipo. Non esistono classi: i metodi possono essere definiti su
qualunque tipo nominato dichiarato nel package corrente.
Sintassi
type Person struct {
Name string
}
func (p Person) Greet() string {
return "ciao " + p.Name
}
p := Person{Name: "Ada"}
fmt.Println(p.Greet()) // "ciao Ada"Il receiver p Person rende Greet un metodo di Person. Dentro al
metodo, p è una variabile come le altre.
Value receiver vs pointer receiver
// VALUE receiver: opera su una COPIA del valore
func (p Person) NameUpper() string {
return strings.ToUpper(p.Name)
}
// POINTER receiver: opera sul valore originale, può mutarlo
func (p *Person) Rename(n string) {
p.Name = n
}Con il pointer receiver puoi modificare il receiver e i cambiamenti si vedono fuori dal metodo. Con il value receiver no — modificheresti solo la copia locale.
p := Person{Name: "Ada"}
p.Rename("Grace")
fmt.Println(p.Name) // "Grace" — modifica visibileQuando usare pointer receiver
Regole pratiche, in ordine di priorità:
- Devi modificare il receiver → pointer receiver. Obbligatorio.
- La struct è grande (decine di campi, array dentro) → pointer receiver per evitare copie costose.
- Coerenza: se ALMENO un metodo del tipo ha pointer receiver, usa pointer receiver anche per gli altri. Convenzione molto importante per le interfacce (Modulo 6).
- Tipi piccoli, immutabili, "valore" (time.Time, complex numbers, piccole struct) → value receiver, leggibile.
Metodi su tipi non-struct
Puoi definire metodi su qualunque tipo NOMINATO dichiarato nel tuo package:
type Celsius float64
func (c Celsius) ToFahrenheit() float64 {
return float64(c)*9/5 + 32
}
t := Celsius(20)
fmt.Println(t.ToFahrenheit()) // 68Non puoi definirli su tipi di altri package (es. int, string): devi
prima creare un tipo nominato locale.
Prova tu
Aggiungi a Person un metodo Greet() string (value receiver) che ritorna 'ciao <Name>'.
Mostra suggerimento
Receiver tra parentesi PRIMA del nome del metodo.
Soluzione disponibile dopo 3 tentativi
Aggiungi un metodo Rename(n string) con POINTER receiver che cambia p.Name.
Mostra suggerimento
Pointer receiver: `(p *Person)`.
Soluzione disponibile dopo 3 tentativi
Quando usare un pointer receiver (p *T)?
func (t ?T) M() { ... }Recap
- Metodo = funzione con un receiver:
func (r T) Nome(...) ... { ... }. - Value receiver: opera su copia; Pointer receiver: opera sull'originale.
- Go fa la conversione
T↔*Tautomaticamente nella chiamata. - Pointer receiver quando: modifica + struct grande + coerenza con altri metodi del tipo.
- Mai mischiare value e pointer receiver sullo stesso tipo.
- I metodi vanno su tipi NOMINATI nel package corrente (anche non-struct).