Получатели-указатели в методах
Изменение состояния структуры через получатели-указатели
«Получатели-указатели в методах» — бесплатный урок 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 — локальная установка не требуется.
Все уроки этого курса
- Что такое указатели
- Указатели и функции
- Получатели-указатели в методах
- new() и когда использовать указатели