Конструктор и экспорт имён
Нулевое значение структуры — как чистый бланк анкеты прямо из типографии. Часто его можно сразу заполнять. Но бывает, что в бланке не хватает целого раздела, и писать в него некуда. Так и в Go: нулевое значение структуры не всегда готово к работе. Поле типа map в нём равно nil, и первая же запись в него завершается паникой:
package main
type Phonebook struct {
numbers map[string]string
}
func main() {
var book Phonebook
book.numbers["Ann"] = "555-01" // ✗
}panic: assignment to entry in nil mapПрограмма собирается, но при запуске завершается паникой на строке с записью: поле numbers — nil-map. Каждый, кто создаёт Phonebook, должен помнить про make. Надёжнее, когда структуру создаёт одна функция, которая заполняет все поля.
Конструктор
Конструктор — обычная функция, которая создаёт значение и возвращает его готовым к работе. Это как окошко в конторе, где вам выдают уже подготовленный бланк со всеми разделами. Особого синтаксиса для конструкторов в Go нет. По соглашению такую функцию называют New плюс имя типа:
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 тогда копируют только адрес, и все методы меняют одну и ту же структуру.
Вызов конструктора
Вызывающий код хранит указатель из конструктора и вызывает методы через него:
book := NewPhonebook()
book.Set("Ann", "555-01")
book.Set("Ann", "555-02")
fmt.Println(book.numbers["Ann"], len(book.numbers)) // 555-02 1Давайте разберём пример по строкам:
book := NewPhonebook()получает указатель на структуру, гдеmapуже есть.- Первый
Setзаписывает номер Ann. - Второй
Setзаменяет его новым номером. fmt.Printlnпечатает555-02 1: ключ один, а номер у него последний.
Экспортированные имена
Видимость имени в Go задаёт регистр его первой буквы. В картинке с анкетой это графы для всех и служебные отметки, которые заполняет только сама контора. Правило одно для функций, типов, полей и методов.
Имя с заглавной буквы
Имя, которое начинается с заглавной буквы, — экспортированное: его видят другие пакеты. Примеры:
fmt.Printlnможно вызвать изmain: имя начинается сP;- если положить
Phonebookв отдельный пакет, другие пакеты смогут вызватьNewPhonebookиSet; - поля
XиYуPointначинаются с заглавной буквы, и другие пакеты могут их менять.
Имя со строчной буквы
Имя со строчной буквы видно только внутри своего пакета. Другие пакеты не доберутся до поля numbers у Phonebook. Заполнить его они не могут, поэтому рабочий Phonebook получают только из конструктора.
Внутри одного пакета видны все имена, и со строчной буквы, и с заглавной. Здесь картинка расходится с Go: «контора» — это весь пакет, а не один тип.
Приватное поле
Этот вопрос часто задают на собеседовании: «Как в Go сделать поле приватным?» Ответ:
- начните имя поля со строчной буквы;
- ключевых слов вроде
privateв Go нет; - видимость задаётся на уровне пакета, а не типа.
Частая ошибка
Имя из чужого пакета пишут со строчной буквы:
fmt.println("hi") // ✗undefined: fmt.println (but have Println)Программа не компилируется: в пакете fmt нет имени println, есть только экспортированная Println. Регистр первой буквы — часть имени, и компилятор подсказывает правильное имя в скобках.
Главное
- Конструктор — обычная функция
NewT, которая возвращает значение, готовое к работе. &T{...}создаёт структуру с заполненными полями и возвращает её адрес.- Имя с заглавной буквы видно другим пакетам, со строчной — только своему пакету.
Подпишись, чтобы пройти курс
Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.