Vai al contenuto
eLearner.app
Modulo 6 · Lezione 4 di 529/50 nel corso~10 min
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

Go
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

Go
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

Go
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 ERR

Senza String(), fmt.Println(OK) stamperebbe 0. Con String(), stampa il nome simbolico. Il tool stringer genera automaticamente questo pattern.

Attenzione alla ricorsione infinita

Go
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

Esercizio#go.m6.l4.e1
Tentativi: 0Caricamento…

Implementa String() su Point in modo che ritorni (X,Y).

Caricamento editor…
Mostra suggerimento

Usa `fmt.Sprintf` per formattare i campi.

Soluzione disponibile dopo 3 tentativi

Esercizio#go.m6.l4.e2
Tentativi: 0Caricamento…

Implementa String() su un tipo Status (int): 0 → 'OK', 1 → 'WARN', altro → 'ERR'.

Caricamento editor…
Mostra suggerimento

Switch sul valore del receiver; ricorda di dare un default.

Soluzione disponibile dopo 3 tentativi

Quiz#go.m6.l4.e3
Pronto

Quale firma deve avere il metodo per soddisfare fmt.Stringer?

Go
func (x T) ??? {}
Opzioni di risposta

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) su t dentro String() (ricorsione).
  • Convenzione: value receiver per String() (no mutazione, evita "sorprese di method set").