Перейти до основного вмісту
Модуль 5 · Урок 2 із 522/50 у курсі~14 min
Модульні уроки (2/5)

Методи: значення проти приймача покажчика

Метод у Go — це функція з приймачем: спец параметр, оголошений між func та назвою методу, який прив’язує метод до типу. Немає класів: можна визначити методи будь-який іменований тип, оголошений у поточному пакеті.

Синтаксис

Go
type Person struct {
    Name string
}

func (p Person) Greet() string {
    return "ciao " + p.Name
}

p := Person{Name: "Ada"}
fmt.Println(p.Greet())   // "ciao Ada"

Одержувач p Person робить Greet методом Person. Всередині p є змінною, як і будь-яка інша.

Приймач значення проти приймача покажчика

Go
// VALUE receiver: operates on a COPY of the value
func (p Person) NameUpper() string {
    return strings.ToUpper(p.Name)
}

// POINTER receiver: operates on the original value, can mutate it
func (p *Person) Rename(n string) {
    p.Name = n
}

За допомогою вказівного приймача ви можете змінювати приймач і вносити зміни видно за межами методу. З приймачем значення ви не можете — ви б змінювати лише локальну копію.

Go
p := Person{Name: "Ada"}
p.Rename("Grace")
fmt.Println(p.Name)   // "Grace" — change is visible

Коли використовувати покажчик-приймач

Практичні правила в порядку пріоритету:

  1. Ви повинні змінити приймач → приймач покажчика. Обов'язковий.
  2. Структура велика (десятки полів, масиви всередині) → покажчик приймача, щоб уникнути дорогих копій.
  3. Узгодженість: якщо ХОТАЙ один метод типу має приймач вказівника, використовуйте приймач вказівника для інших теж. Дуже важлива конвенція для інтерфейси (Модуль 6).
  4. Малі, незмінні, "значення" типи (час. Час, комплексні числа, малі structs) → приймач значення, більш читабельний.

Методи неструктурних типів

Ви можете визначити методи для будь-якого типу NAMED, оголошеного у вашому пакеті:

Go
type Celsius float64

func (c Celsius) ToFahrenheit() float64 {
    return float64(c)*9/5 + 32
}

t := Celsius(20)
fmt.Println(t.ToFahrenheit())  // 68

Ви не можете визначити їх для типів з інших пакунків (наприклад, int, string): ви повинні спочатку створіть локальний іменований тип.

Спробуйте

вправи#go.m5.l2.e1
Спроби: 0Завантаження…

Додайте рядковий метод Greet() (одержувач значення) до Person, який повертає «ciao <Name>».

Завантаження редактора…
Показати підказку

Одержувач у дужках ПЕРЕД назвою методу.

Рішення доступне після 3 спроб

вправи#go.m5.l2.e2
Спроби: 0Завантаження…

Додайте метод Rename(n string) із приймачем POINTER, який змінює p.Name.

Завантаження редактора…
Показати підказку

Одержувач покажчика: `(p *Person)`.

Рішення доступне після 3 спроб

Quiz#go.m5.l2.e3
Готовий

Коли слід використовувати приймач вказівника (p *T)?

Go
func (t ?T) M() { ... }
Варіанти відповідей

Підведення підсумків

  • Метод = функція з приймачем: func (r T) Name(...) ... { ... }.
  • Одержувач вартості: працює з копією; Приймач вказівника: працює з оригіналом.
  • Go автоматично виконує перетворення T ↔ *T на місці виклику.
  • Приймач покажчика, коли: мутація + велика структура + узгодженість з іншими методами типу.
  • Ніколи не змішуйте приймачі значень і покажчиків одного типу.
  • Методи йдуть на NAMED типи в поточному пакеті (включаючи non-struct).