0Pricing
Go Academy · Урок

Получатели-указатели в методах

Изменение состояния структуры через получатели-указатели

«Получатели-указатели в методах» — бесплатный урок Go Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Go Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Go Academy содержит 4 уроков всего.

Итоги: получатель-значение и получатель-указатель

Получатель-значение работает с копией структуры. Получатель-указатель работает с исходной структурой. Используйте получатели-указатели всегда, когда методу нужно изменять состояние.

Изменение данных через получателя-указателя

Изменять структуру может только получатель-указатель:

package main
import "fmt"

type Stack struct{ items []int }

func (s *Stack) Push(v int) { s.items = append(s.items, v) }
func (s *Stack) Pop() int {
    n := len(s.items) - 1
    v := s.items[n]
    s.items = s.items[:n]
    return v
}

func main() {
    var st Stack
    st.Push(1); st.Push(2)
    fmt.Println(st.Pop()) // 2
}

Правило единообразия

Если для какого-либо метода типа нужен получатель-указатель, все методы должны использовать получатели-указатели. Смешивание этих вариантов вызывает проблемы с соответствием интерфейсам:

package main
import "fmt"

type Counter struct{ n int }

func (c *Counter) Inc()   { c.n++ }   // pointer
func (c *Counter) Val() int { return c.n } // pointer too

func main() {
    c := &Counter{}
    c.Inc(); c.Inc()
    fmt.Println(c.Val()) // 2
}

Соответствие интерфейсу

Метод с получателем-указателем входит в набор методов только типа *T, но не типа T. Это влияет на соответствие интерфейсам:

package main
import "fmt"

type Writer interface{ Write([]byte) }

type File struct{ name string }
func (f *File) Write(b []byte) { fmt.Println(string(b)) }

func main() {
    var w Writer = &File{"out.txt"} // OK
    // var w Writer = File{} — compile error
    w.Write([]byte("hello"))
}

Автоматическое получение адреса

При вызове метода с получателем-указателем для адресуемого значения Go автоматически получает его адрес:

package main
import "fmt"

type Box struct{ V int }
func (b *Box) Double() { b.V *= 2 }

func main() {
    b := Box{5}
    b.Double()          // Go does (&b).Double()
    fmt.Println(b.V)    // 10
}

Неадресуемые значения

Нельзя вызывать методы с получателем-указателем для неадресуемых значений (элементов отображения и возвращаемых функцией значений, не присвоенных переменным):

package main

type T struct{ V int }
func (t *T) Set(v int) { t.V = v }

func getT() T { return T{} }

func main() {
    var t T
    t.Set(1)       // OK — t is addressable
    // getT().Set(1) — ERROR: cannot take address of getT()
    _ = t
}

Получатели больших структур

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

package main

type Image struct{ pixels [1024][1024]uint8 }

// Value receiver copies 1MB per call!
func (img Image) Bad() uint8 { return img.pixels[0][0] }

// Pointer receiver — zero copy
func (img *Image) Good() uint8 { return img.pixels[0][0] }

func main() { _ = Image{} }

Получатели-указатели с нулевым значением

Методы с получателями-указателями могут корректно обрабатывать нулевое значение:

package main
import "fmt"

type List struct{ head *node }
type node struct{ val int; next *node }

func (l *List) Len() int {
    if l == nil { return 0 }
    count := 0
    for n := l.head; n != nil; n = n.next { count++ }
    return count
}

func main() {
    var l *List
    fmt.Println(l.Len()) // 0
}

Встраивание и получатели-указатели

При встраивании типа с получателями-указателями внешнюю структуру также нужно использовать через указатель, чтобы получить продвигаемые методы:

package main
import "fmt"

type Base struct{ V int }
func (b *Base) Set(v int) { b.V = v }

type Derived struct{ Base }

func main() {
    d := &Derived{}
    d.Set(42)           // promoted pointer-receiver method
    fmt.Println(d.V)    // 42
}

Копирование мьютексов

Никогда не копируйте структуру, содержащую sync.Mutex — всегда используйте получатель-указатель или передавайте её через указатель. Копирование заблокированного мьютекса приводит к взаимной блокировке:

package main
import "sync"

type Safe struct {
    mu sync.Mutex
    v  int
}

// Correct: pointer receiver
func (s *Safe) Set(v int) {
    s.mu.Lock()
    defer s.mu.Unlock()
    s.v = v
}

func main() { _ = Safe{} }

go vet обнаруживает ошибки

Запустите go vet ./..., чтобы обнаружить распространённые ошибки, связанные с получателями-указателями, например копирование мьютексов и блокировок. Это часть конвейеров go build и проверок непрерывной интеграции.

Быстрая проверка

В набор методов каких типов входит метод с получателем-указателем?

Итоги

Главные выводы:

  • Получатели-указатели изменяют данные, а получатели-значения — нет
  • Используйте для всех методов типа один и тот же вид получателя
  • Только *T соответствует интерфейсам, требующим методов с получателями-указателями
  • Никогда не копируйте структуры с мьютексами — используйте получатели-указатели

Практическое задание

Реализуйте структуру Queue с методами Enqueue(int), Dequeue() (int, bool) и Len() int, использующими получатели-указатели и основанными на срезе []int.

Часто задаваемые вопросы

Урок «Получатели-указатели в методах» бесплатный?

Да — полный текст урока «Получатели-указатели в методах» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Go Academy, подпишись на CoddyKit PRO. Курс Go Academy содержит 4 уроков всего.

Чему я научусь в уроке «Получатели-указатели в методах»?

Изменение состояния структуры через получатели-указатели Ты практикуешь Go Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Go Academy?

Предыдущий опыт не требуется. Go Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.

Сколько времени занимает урок «Получатели-указатели в методах»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Go Academy?

Да. Каждый урок Go Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Что такое указатели
  2. Указатели и функции
  3. Получатели-указатели в методах
  4. new() и когда использовать указатели
← Назад к Go Academy