← Назад к вопросам

Что делает reciever со звёздочкой?

2.0 Middle🔥 301 комментариев
#Основы Go

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI7 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что делает receiver со звёздочкой (*) в Go?

Receiver со звёздочкой — это метод с получателем-указателем, который позволяет методу работать с самим экземпляром структуры, а не с его копией. Это фундаментальная концепция в Go, которая влияет на семантику, производительность и поведение методов.

Ключевые отличия от receiver по значению

Receiver по значениюReceiver по указателю
Работает с копией структурыРаботает с оригинальной структурой
Изменения не сохраняютсяИзменения сохраняются
Может вызываться для указателей и значенийМожет вызываться для указателей и значений (Go автоматически преобразует)

Основные аспекты receiver-указателя

1. Изменение состояния структуры

Receiver-указатель позволяет модифицировать поля структуры, так как метод получает ссылку на оригинальный объект, а не на его копию.

type Counter struct {
    value int
}

// Receiver по значению - изменения не сохранятся
func (c Counter) IncrementByValue() {
    c.value++ // Работает с копией
}

// Receiver по указателю - изменения сохранятся
func (c *Counter) IncrementByPointer() {
    c.value++ // Работает с оригиналом
}

func main() {
    c := Counter{value: 0}
    
    c.IncrementByValue()
    fmt.Println(c.value) // 0 - не изменилось
    
    c.IncrementByPointer()
    fmt.Println(c.value) // 1 - изменилось
}

2. Эффективность работы с большими структурами

Для больших структур использование receiver-указателя предотвращает копирование всей структуры при каждом вызове метода, что значительно улучшает производительность.

type LargeStruct struct {
    data [1000000]int
}

// Неэффективно - копируется весь массив
func (s LargeStruct) ProcessByValue() {
    // Работа с копией 1 000 000 элементов
}

// Эффективно - передается только указатель
func (s *LargeStruct) ProcessByPointer() {
    // Работа с оригинальной структурой
}

3. Реализация интерфейсов

Receiver-указатель влияет на то, какие типы могут реализовывать интерфейсы. Если метод определен с receiver-указателем, то интерфейс может быть реализован только указателем на тип.

type Speaker interface {
    Speak() string
}

type Person struct {
    name string
}

// Receiver-указатель
func (p *Person) Speak() string {
    return "Hello, I'm " + p.name
}

func main() {
    var s Speaker
    
    p1 := Person{name: "Alice"}
    // s = p1 // Ошибка компиляции: Person не реализует Speaker
    s = &p1 // OK - указатель реализует интерфейс
    
    p2 := &Person{name: "Bob"}
    s = p2 // OK
}

Когда использовать receiver-указатель?

  1. Когда нужно модифицировать receiver — если метод должен изменять состояние структуры.
  2. Для оптимизации производительности — при работе с большими структурами, чтобы избежать их копирования.
  3. Для согласованности — если часть методов уже использует receiver-указатель, следует придерживаться единого стиля.
  4. При использовании мьютексов и синхронизации — чтобы блокировки применялись к одному экземпляру.
type SafeCounter struct {
    mu    sync.Mutex
    count int
}

// Правильно: receiver-указатель для синхронизации
func (sc *SafeCounter) Increment() {
    sc.mu.Lock()
    defer sc.mu.Unlock()
    sc.count++
}

Важное замечание о вызовах методов

Go автоматически обрабатывает вызовы методов, позволяя вызывать методы с receiver-указателем как для значений, так и для указателей:

type Point struct {
    x, y int
}

func (p *Point) Move(dx, dy int) {
    p.x += dx
    p.y += dy
}

func main() {
    p1 := Point{1, 2}
    p2 := &Point{3, 4}
    
    // Оба вызова работают благодаря автоматическому преобразованию
    p1.Move(10, 10) // Go автоматически берет адрес: (&p1).Move(...)
    p2.Move(5, 5)   // Указатель используется напрямую
    
    fmt.Println(p1) // {11 12}
    fmt.Println(p2) // &{8 9}
}

Когда НЕ использовать receiver-указатель?

  1. Для неизменяемых типов — если структура должна быть иммутабельной.
  2. Для мелких структур — где затраты на копирование незначительны.
  3. Для reference-типов — слайсы, мапы, каналы, функции и интерфейсы уже являются reference-типами.

В итоге, receiver со звёздочкой — это мощный механизм, который обеспечивает:

  • Возможность модификации оригинального объекта
  • Повышение производительности для больших структур
  • Гибкость при работе с интерфейсами
  • Согласованное поведение методов

Правильный выбор между receiver по значению и по указателю зависит от конкретной задачи, размера структуры и желаемой семантики работы с объектом.