Перейти к содержимому
Теория · Структуры и методы · урок 7 из 11

Конструктор и экспорт имён

Нулевое значение структуры — как чистый бланк анкеты прямо из типографии. Часто его можно сразу заполнять. Но бывает, что в бланке не хватает целого раздела, и писать в него некуда. Так и в Go: нулевое значение структуры не всегда готово к работе. Поле типа map в нём равно nil, и первая же запись в него завершается паникой:

GO
package main

type Phonebook struct {
    numbers map[string]string
}

func main() {
    var book Phonebook
    book.numbers["Ann"] = "555-01" // ✗
}
TEXT
panic: assignment to entry in nil map

Программа собирается, но при запуске завершается паникой на строке с записью: поле numbers — nil-map. Каждый, кто создаёт Phonebook, должен помнить про make. Надёжнее, когда структуру создаёт одна функция, которая заполняет все поля.

Конструктор

Конструктор — обычная функция, которая создаёт значение и возвращает его готовым к работе. Это как окошко в конторе, где вам выдают уже подготовленный бланк со всеми разделами. Особого синтаксиса для конструкторов в Go нет. По соглашению такую функцию называют New плюс имя типа:

GO
type Phonebook struct {
    numbers map[string]string
}

func NewPhonebook() *Phonebook {
    return &Phonebook{numbers: make(map[string]string)}
}

func (p *Phonebook) Set(name, number string) {
    p.numbers[name] = number
}

Главная строка здесь — return &Phonebook{numbers: make(map[string]string)}. Разберём её по частям:

  • make(map[string]string) создаёт пустую map, в которую уже можно писать;
  • Phonebook{numbers: …} — литерал структуры с заполненным полем numbers;
  • & перед литералом создаёт новую структуру и возвращает её адрес. Он работает как new, но с заполненными полями.

Конструктор возвращает указатель — бумажку с адресом готовой анкеты. Копии book тогда копируют только адрес, и все методы меняют одну и ту же структуру.

Вызов конструктора

Вызывающий код хранит указатель из конструктора и вызывает методы через него:

GO
book := NewPhonebook()
book.Set("Ann", "555-01")
book.Set("Ann", "555-02")
fmt.Println(book.numbers["Ann"], len(book.numbers)) // 555-02 1

Давайте разберём пример по строкам:

  1. book := NewPhonebook() получает указатель на структуру, где map уже есть.
  2. Первый Set записывает номер Ann.
  3. Второй Set заменяет его новым номером.
  4. fmt.Println печатает 555-02 1: ключ один, а номер у него последний.

Экспортированные имена

Видимость имени в Go задаёт регистр его первой буквы. В картинке с анкетой это графы для всех и служебные отметки, которые заполняет только сама контора. Правило одно для функций, типов, полей и методов.

Имя с заглавной буквы

Имя, которое начинается с заглавной буквы, — экспортированное: его видят другие пакеты. Примеры:

  • fmt.Println можно вызвать из main: имя начинается с P;
  • если положить Phonebook в отдельный пакет, другие пакеты смогут вызвать NewPhonebook и Set;
  • поля X и Y у Point начинаются с заглавной буквы, и другие пакеты могут их менять.

Имя со строчной буквы

Имя со строчной буквы видно только внутри своего пакета. Другие пакеты не доберутся до поля numbers у Phonebook. Заполнить его они не могут, поэтому рабочий Phonebook получают только из конструктора.

Внутри одного пакета видны все имена, и со строчной буквы, и с заглавной. Здесь картинка расходится с Go: «контора» — это весь пакет, а не один тип.

Приватное поле

Этот вопрос часто задают на собеседовании: «Как в Go сделать поле приватным?» Ответ:

  • начните имя поля со строчной буквы;
  • ключевых слов вроде private в Go нет;
  • видимость задаётся на уровне пакета, а не типа.

Частая ошибка

Имя из чужого пакета пишут со строчной буквы:

GO
fmt.println("hi") // ✗
TEXT
undefined: fmt.println (but have Println)

Программа не компилируется: в пакете fmt нет имени println, есть только экспортированная Println. Регистр первой буквы — часть имени, и компилятор подсказывает правильное имя в скобках.

Главное

  • Конструктор — обычная функция NewT, которая возвращает значение, готовое к работе.
  • &T{...} создаёт структуру с заполненными полями и возвращает её адрес.
  • Имя с заглавной буквы видно другим пакетам, со строчной — только своему пакету.

Подпишись, чтобы пройти курс

Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.

Войти другим способом