Интерфейс fmt.Stringer

Интерфейс fmt.Stringer определен в пакете fmt и позволяет типам управлять тем, как они представляются в виде строки. Когда тип реализует этот интерфейс, функции пакета fmt, такие как fmt.Println, fmt.Sprintf и другие, будут использовать метод String для получения строкового представления значения.

Синтаксис

Интерфейс fmt.Stringer объявлен следующим образом:

gotype Stringer interface {
  String() string
}

Любой тип, который определяет метод String с сигнатурой func String() string, автоматически реализует этот интерфейс.

Пример

Давайте создадим пользовательский тип Person и реализуем для него интерфейс fmt.Stringer:

gopackage main

import "fmt"

type Person struct {
  Name string
  Age  int
}

func (p Person) String() string {
  return fmt.Sprintf("Person{Name: %s, Age: %d}", p.Name, p.Age)
}

func main() {
  p := Person{Name: "Alice", Age: 30}
  fmt.Println(p)
}

Результат выполнения кода:

goPerson{Name: Alice, Age: 30}

Пример

Теперь рассмотрим тип IPAddress, который хранит IP-адрес как массив байтов и возвращает его строковое представление:

gopackage main

import "fmt"

type IPAddress [4]byte

func (ip IPAddress) String() string {
  return fmt.Sprintf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3])
}

func main() {
  ip := IPAddress{192, 168, 1, 1}
  fmt.Println(ip)
}

Результат выполнения кода:

go192.168.1.1

Пример

Важно отметить, что если метод String определен с указателем в качестве получателя, то интерфейс будет реализован только для указателей на тип, а не для самих значений:

gopackage main

import "fmt"

type User struct {
  ID   int
  Name string
}

func (u *User) String() string {
  return fmt.Sprintf("User{ID: %d, Name: %s}", u.ID, u.Name)
}

func main() {
  u1 := &User{ID: 1, Name: "Bob"} // указатель
  fmt.Println(u1)

  u2 := User{ID: 2, Name: "Charlie"} // значение
  fmt.Println(u2)
}

Результат выполнения кода:

goUser{ID: 1, Name: Bob}
{2 Charlie}

Второй вызов выводит стандартное представление структуры, так как интерфейс fmt.Stringer не реализован для значений типа User, а только для указателей на них.

Пример

Использование интерфейса fmt.Stringer с функциями пакета fmt для форматированного вывода:

gopackage main

import "fmt"

type Point struct {
  X int
  Y int
}

func (p Point) String() string {
  return fmt.Sprintf("(%d, %d)", p.X, p.Y)
}

func main() {
  p1 := Point{X: 10, Y: 20}
  p2 := Point{X: 5, Y: 15}

  fmt.Printf("Point 1: %v\n", p1)
  fmt.Printf("Point 2: %v\n", p2)
  fmt.Printf("Combined: %v and %v\n", p1, p2)
}

Результат выполнения кода:

goPoint 1: (10, 20)
Point 2: (5, 15)
Combined: (10, 20) and (5, 15)

Смотрите также

  • интерфейс interface,
    который определяет базовое понятие интерфейсов в Go
  • интерфейс error,
    который используется для представления ошибок в виде строки
  • концепцию implementation,
    описывающую реализацию интерфейсов в Go
  • конструкцию type assertion,
    которая позволяет извлекать значение конкретного типа из интерфейса