Lecții din modul (4/5)
Interfața Stringer
fmt.Stringer este probabil cea mai folosită interfață standard din toate
Biblioteca standard a lui Go. Este demonstrația perfectă a puterii
de interfețe mici.
Definiţie
type Stringer interface {
String() string
}O singură semnătură. Orice tip care definește String() string satisface
ea. Și fmt recunoaște această interfață: când îi transmiteți o valoare către
fmt.Print*, dacă implementează Stringer, rezultatul String()
este utilizat în locul reprezentării implicite.
Exemplu
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)Aceeași valoare se imprimă cu formatare personalizată în %v, %s, Println,
Sprint, log etc. Toate doar prin definirea unei singure metode.
Când are sens
- Tipuri care modelează un domeniu (ID-uri, coduri, coordonate, enumerari).
- Erori cu o reprezentare care poate fi citită de om (vezi următoarea lecție despre
error). - Tipuri pe care le înregistrați des și pentru care doriți un format fix.
Nu pentru toate: dacă tipul este simplu și reprezentarea implicită este în regulă, nu adăugați zgomot.
Tipuri de enumerare: model comun cu 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 ERRFără String(), fmt.Println(OK) ar tipări 0. Cu String(),
imprimă numele simbolic. stringer
instrumentul generează acest model automat.
Atenție la recursiunea infinită
type T int
func (t T) String() string {
return fmt.Sprintf("%d", int(t)) // OK: cast to int breaks the recursion
}Stringer pe un receptor pointer
Dacă String() are un receptor pointer, numai *T satisface Stringer.
Cu fmt.Println(t) (o valoare), Go se uită la setul de metode T,
nu îl găsește și folosește formatul implicit. Pentru a evita acest lucru, valoare
receptor este de obicei folosit pentru String() (este o „vizualizare”; nu
modifica orice).
Încearcă
Implementați String() pe Point astfel încât să returneze (X,Y).
Afișează indiciu
Utilizați `fmt.Sprintf` pentru a formata câmpurile.
Soluție disponibilă după 3 încercări
Implementați String() pe un tip Status (int): 0 → „OK”, 1 → „WARN”, în caz contrar → „ERR”.
Afișează indiciu
Porniți valoarea receptorului; nu uitați să furnizați o valoare implicită.
Soluție disponibilă după 3 încercări
Ce semnătură trebuie să aibă metoda pentru a satisface fmt.Stringer?
func (x T) ??? {}Recapitulare
fmt.Stringer = interface { String() string }.- Implementarea acestuia modifică modul în care
fmt.Print*reprezintă tipul. - Idiomatic pentru enumerari (
iota), tipuri de domenii, coduri/identificatori. - Nu apelați niciodată
fmt.Sprintf("%s", t)petîn interiorulString()(recursiune). - Convenție: receptor de valoare pentru
String()(fără mutație, evită „surprizele stabilite prin metoda”).