Lezioni del modulo (4/5)
L’interfaccia Stringer
fmt.Stringer è probabilmente l'interfaccia standard più usata di tutta
la libreria standard di Go. Ne è la dimostrazione perfetta del power
delle interfacce piccole.
Definizione
type Stringer interface {
String() string
}Una sola firma. Qualunque tipo che definisca String() string la
soddisfa. E fmt riconosce questa interfaccia: quando passi un valore
a fmt.Print*, se implementa Stringer, viene usato il risultato di
String() invece della rappresentazione di default.
Esempio
type Point struct{ X, Y int }
func (p Point) String() string {
return fmt.Sprintf("(%d,%d)", p.X, p.Y)
}
fmt.Println(Point{1, 2}) // (1,2)
fmt.Printf("%v %s\n", Point{3, 4}, Point{5, 6})
// (3,4) (5,6)Stesso valore stampa formattata custom in %v, %s, Println, Sprint,
log, ecc. Tutto solo definendo un metodo.
Quando ha senso
- Tipi che modellano un dominio (id, codici, coordinate, enumerazioni).
- Errori con rappresentazione human-readable (vedi prossima lezione su
error). - Tipi che vuoi loggare spesso e desideri un formato fisso.
Non per tutto: se il tipo è semplice e la rappresentazione di default va bene, non aggiungere noise.
Tipi enum: pattern comune con iota
type Status int
const (
OK Status = iota
WARN
ERR
)
func (s Status) String() string {
switch s {
case OK: return "OK"
case WARN: return "WARN"
case ERR: return "ERR"
default: return "UNKNOWN"
}
}
fmt.Println(OK, WARN, ERR) // OK WARN ERRSenza String(), fmt.Println(OK) stamperebbe 0. Con String(),
stampa il nome simbolico. Il tool stringer
genera automaticamente questo pattern.
Attenzione alla ricorsione infinita
type T int
func (t T) String() string {
return fmt.Sprintf("%d", int(t)) // OK: cast a int interrompe la ricorsione
}Stringer su pointer receiver
Se String() ha pointer receiver, solo *T soddisfa Stringer. Con
fmt.Println(t) (valore), Go cerca il method set di T, non lo trova,
e usa il formato di default. Per evitarlo, di solito si usa value
receiver per String() (è una "view"; non modifica nulla).
Prova tu
Implementa String() su Point in modo che ritorni (X,Y).
Mostra suggerimento
Usa `fmt.Sprintf` per formattare i campi.
Soluzione disponibile dopo 3 tentativi
Implementa String() su un tipo Status (int): 0 → 'OK', 1 → 'WARN', altro → 'ERR'.
Mostra suggerimento
Switch sul valore del receiver; ricorda di dare un default.
Soluzione disponibile dopo 3 tentativi
Quale firma deve avere il metodo per soddisfare fmt.Stringer?
func (x T) ??? {}Recap
fmt.Stringer = interface { String() string }.- Implementarla cambia come
fmt.Print*rappresenta il tipo. - Idiomatico per enum (
iota), tipi di dominio, codici/identificatori. - Mai chiamare
fmt.Sprintf("%s", t)sutdentroString()(ricorsione). - Convenzione: value receiver per
String()(no mutazione, evita "sorprese di method set").