Методы и receivers
Тема дорожной карты · Golang
Методы в Go — функции, связанные с именованным типом через параметр-получатель; они записываются как func (r ReceiverType) MethodName(args) returnType и являются основным механизмом прикрепления поведения к структурам и другим типам в Golang. Метод может иметь value receiver (v T) или pointer receiver (p *T): pointer receivers предпочтительны, когда метод изменяет состояние или копирование структуры было бы дорогостоящим, а value receivers идиоматичны для небольших неизменяемых типов. Методы в Go удовлетворяют интерфейсам неявно — любой тип, предоставляющий необходимый набор методов, удовлетворяет интерфейсу без явного объявления, что является основой полиморфизма в Golang. Наборы методов определяют, какие интерфейсы удовлетворяет тип: набор методов value type содержит только методы с value receiver, тогда как набор pointer type содержит методы как с value, так и с pointer receiver. Написание чётких, хорошо именованных методов в Go необходимо для построения читаемых API, составных цепочек http.Handler и поддерживаемых бэкенд-сервисов.
Как это работает
Методы и receivers покрывает встроенное: массивы (фиксированный размер — редко в идиоматичном Go), slices (динамические массивы — рабочая лошадка), maps (map[K]V, на хешах), strings (UTF-8 байтовые последовательности, неизменяемые). Struct — пользовательские типы с именованными полями; методы на типах (func (r Receiver) Method()); интерфейсы декларируют наборы методов (удовлетворяются неявно — нет ключевого слова implements). Указатели через & и *; slices и maps уже содержат указатели внутри.
Когда применять
Slices везде, где массивы соблазняют — почти всегда правильный ответ. Map — для lookup по ключу; помните, что Go-map не упорядочены. Embedding struct — для композиции (нет эквивалента наследования). Маленькие интерфейсы (1-3 метода) — Go-интерфейсы лучше когда обнаружены, чем объявлены (выносите, когда есть 2+ реализаций).
Типичные ошибки
Ловушки Методы и receivers: slice-граблик — append(s, x) может или не может делить underlying массив с s (при реалокации не делит — тихие баги); for _, v := range slice { go func() { use(v) }() } захватывает v по ссылке до 1.22 (closures видят последнее значение — починено в 1.22, но многие туториалы до этого); nil-maps паникуют на запись (make(map[K]V)); огромные interface-типы убивают идиому "implicitly satisfied".