Перейти до основного вмісту
Модуль 6 · Урок 4 із 529/50 у курсі~10 min
Модульні уроки (4/5)

Інтерфейс Stringer

fmt.Stringer, ймовірно, найбільш використовуваний стандартний інтерфейс у всіх Стандартна бібліотека Go. Це ідеальна демонстрація сили малих інтерфейсів.

Визначення

Go
type Stringer interface {
    String() string
}

Єдиний підпис. Будь-який тип, який визначає String() string, задовольняє це. І fmt розпізнає цей інтерфейс: коли ви передаєте значення fmt.Print*, якщо він реалізує Stringer, результат String() використовується замість представлення за замовчуванням.

приклад

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)

Те саме значення друкується зі спеціальним форматуванням у %v, %s, Println, Sprint, log і т. д. Все, просто визначивши один метод.

Коли це має сенс

  • Типи, які моделюють домен (ідентифікатори, коди, координати, перерахування).
  • Помилки зі зрозумілим для людини представленням (див. наступний урок на error).
  • Типи, які ви часто входите в журнал і для яких вам потрібен фіксований формат.

Не для всіх: якщо тип простий і представлення за замовчуванням добре, не додавайте шуму.

Типи переліків: загальний шаблон із 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

Без String() fmt.Println(OK) надрукував би 0. З String(), він друкує символічне ім'я. stringer інструмент генерує цей шаблон автоматично.

Слідкуйте за нескінченною рекурсією

Go
type T int
func (t T) String() string {
    return fmt.Sprintf("%d", int(t))  // OK: cast to int breaks the recursion
}

Стрінгер на стрілочній коробці

Якщо String() має приймач вказівника, лише *T задовольняє Stringer. За допомогою fmt.Println(t) (значення) Go переглядає набір методів T, не знаходить його та використовує стандартний формат. Щоб уникнути цього, value приймач зазвичай використовується для String() (це "перегляд"; він не змінити будь-що).

Спробуйте

вправи#go.m6.l4.e1
Спроби: 0Завантаження…

Реалізуйте String() на Point, щоб він повертав (X,Y).

Завантаження редактора…
Показати підказку

Для форматування полів використовуйте `fmt.Sprintf`.

Рішення доступне після 3 спроб

вправи#go.m6.l4.e2
Спроби: 0Завантаження…

Реалізуйте String() на типі Status (int): 0 → «OK», 1 → «WARN», інакше → «ERR».

Завантаження редактора…
Показати підказку

Увімкніть значення приймача; не забудьте вказати значення за замовчуванням.

Рішення доступне після 3 спроб

Quiz#go.m6.l4.e3
Готовий

Який підпис повинен мати метод, щоб задовольнити fmt.Stringer?

Go
func (x T) ??? {}
Варіанти відповідей

Підведення підсумків

  • fmt.Stringer = interface { String() string }.
  • Його впровадження змінює те, як fmt.Print* представляє тип.
  • Ідіоматичні для переліків (iota), типів доменів, кодів/ідентифікаторів.
  • Ніколи не викликайте fmt.Sprintf("%s", t) на t всередині String() (рекурсія). — Угода: приймач значення для String() (без мутацій, уникає «сюрпризів набору методів»).