Методы

Методы привязывают поведение к типу. Главный вопрос: получатель — значение или указатель?

Метод — это функция с особым параметром-получателем (receiver), который пишут перед именем метода. Он связывает функцию с типом.

Что такое метод

Метод — обычная функция, но с получателем в скобках до имени. Получатель указывает, к какому типу относится метод. Вызывают метод через точку.

package main

import "fmt"

type Rectangle struct {
    Width, Height float64
}

// (r Rectangle) — получатель-значение
func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

func main() {
    rect := Rectangle{Width: 3, Height: 4}
    fmt.Println(rect.Area())
}

Вывод:

12

Главный вопрос: value vs pointer receiver

Получатель бывает двух видов, и выбор между ними — ключевое решение.

  • Получатель-значение (r Rectangle) работает с копией. Изменения внутри метода не затрагивают оригинал. Подходит для методов, которые только читают.
  • Получатель-указатель (r *Rectangle) работает с оригиналом. Может его менять. Нужен, когда метод изменяет состояние.
package main

import "fmt"

type Counter struct {
    value int
}

// получатель-значение: меняет копию (бесполезно)
func (c Counter) IncBroken() { c.value++ }

// получатель-указатель: меняет оригинал
func (c *Counter) Inc() { c.value++ }

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

    c.Inc()
    c.Inc()
    fmt.Println(c.value) // 2 — изменилось
}

Вывод:

0
2

Здесь IncBroken увеличивает счётчик в копии, и оригинал остаётся нулём — классическая ошибка новичка. Inc с указателем работает правильно.

Удобство Go

Go автоматически берёт адрес при вызове метода с указателем: можно писать c.Inc(), а не (&c).Inc(). Компилятор подставит & сам, если переменная адресуема.

Какое правило выбрать

Практическое правило: если хотя бы один метод типа использует указатель — делайте указателями все методы этого типа для единообразия. Указатель также предпочтителен для больших структур (чтобы не копировать) и когда метод меняет состояние.

ПолучательКогда использовать
значение (r T)метод только читает, тип маленький
указатель (r *T)метод меняет состояние или тип большой

Итог

  • Метод — функция с получателем перед именем; вызывается через точку.
  • Получатель-значение меняет копию, получатель-указатель — оригинал.
  • Меняете состояние или тип большой — берите указатель; держите получатели единообразными.
Проверьте себя
1. В чём разница между получателем-значением и получателем-указателем?
AНикакой разницы нет
BЗначение работает с копией, указатель — с оригиналом и может его менять
CУказатель только читает, значение пишет
DУказатель работает медленнее всегда
2. Метод func (c Counter) Inc() { c.value++ } увеличивает оригинальный счётчик?
AДа
BНет, он меняет копию, оригинал не затрагивается
CТолько при первом вызове
DЗависит от размера структуры
3. Какое практическое правило по выбору получателей?
AВсегда только значения
BЕсли хотя бы один метод типа на указателе — делать указателями все методы
CСлучайно чередовать
DУказатели только для чтения
Поддержать проект