логотип PurpleSchool
логотип PurpleSchool

Функция make в Go

Автор

Александр Гольцман

Функция make в Go используется для создания срезов, карт и каналов. Она позволяет заранее выделить память и настроить структуру данных для работы, что особенно полезно при оптимизации производительности.

В этой статье я покажу, как работает make, в каких случаях ее стоит использовать и чем она отличается от new. Также разберем примеры применения этой функции в реальном коде.

Зачем нужна функция make

В Go выделение памяти и управление структурами данных устроено довольно просто. Однако, когда речь идет о срезах, картах и каналах, стандартные подходы не всегда удобны.

Функция make позволяет:

  • Создавать срезы с предопределенной длиной и емкостью.
  • Инициализировать карты (map), избегая нулевых значений.
  • Создавать каналы с указанным буфером.

Важно понимать, что make не просто выделяет память, а именно инициализирует структуры, делая их готовыми к использованию.

Использование make для создания срезов

Срезы (slice) в Go — это динамические массивы, которые могут изменять свою длину. С помощью make можно создать срез нужного размера и задать его емкость:

s := make([]int, 5, 10)

Здесь:

  • 5 — начальная длина среза (сколько элементов сразу доступно).
  • 10 — емкость (сколько элементов можно добавить без перераспределения памяти).

Если емкость не указывать, она будет равна длине:

s := make([]int, 5) // Длина 5, емкость тоже 5

Смотрите, как работает изменение длины среза:

s = append(s, 42)
fmt.Println(len(s), cap(s)) // Длина 6, емкость 10

Создание map с помощью make

Карты (map) в Go — это структуры для хранения данных в формате "ключ-значение". make нужен, чтобы инициализировать карту перед использованием:

m := make(map[string]int)
m["one"] = 1
m["two"] = 2

Без make карта будет nil, и любое обращение к ней вызовет ошибку:

var m map[string]int
m["one"] = 1 // Ошибка: panic: assignment to entry in nil map

С помощью make можно задать начальную емкость (хотя она динамически увеличивается при необходимости):

m := make(map[string]int, 100) // Ожидаем, что будет ~100 элементов

Создание каналов через make

Каналы (channel) используются для передачи данных между горутинами. Без make они не работают.

Простой пример создания канала:

ch := make(chan int)

Если канал буферизированный, можно указать размер буфера:

ch := make(chan int, 3)

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

ch <- 1
ch <- 2
ch <- 3
// ch <- 4 // Это вызовет блокировку, так как буфер заполнен

Смотрите, как работает чтение из канала:

fmt.Println(<-ch) // 1
fmt.Println(<-ch) // 2
fmt.Println(<-ch) // 3

Чем make отличается от new

Go также имеет функцию new, но она работает иначе.

  • make инициализирует объекты (срезы, карты, каналы).
  • new просто выделяет память, но не инициализирует структуру.

Смотрите, в чем разница:

s1 := make([]int, 5)  // Готовый срез
s2 := new([]int)      // Указатель на nil-срез

s2 — это *[]int, и с ним нельзя сразу работать, его нужно дополнительно инициализировать.

Так что в большинстве случаев make удобнее.

Выводы

Функция make — это мощный инструмент для создания динамических структур в Go.

  • Используйте make для срезов, если хотите задать начальную длину и емкость.
  • Для карт make обязателен, иначе карта будет nil.
  • Каналы тоже требуют make, иначе они не будут работать.
  • Не путайте make и new: make инициализирует объекты, new просто выделяет память.

Смотрите, какие структуры вам нужны, и используйте make, когда требуется инициализация.

Стрелочка влевоСоздание Slice в GolangLen и Cap в GolangСтрелочка вправо

Все гайды по Golang

Работа с YAML в GolangПреобразование типов в GolangКонвертация структур в JSON в GolangStrconv в GolangИспользование пакета SQLx для работы с базами данных в GolangРазбираемся с SQL в GolangРазделение строк с помощью функции split в GolangSort в GoПоиск и замена строк в Go - GolangИспользование пакета reflect в GolangРабота с PostgreSQL в GoPointers в GolangПарсинг в GoРабота со списками (list) в GolangПреобразование int в string в GolangРабота с числами с плавающей точкой в GolangРабота с полями в GolangИспользование enum в GolangОбработка JSON в GoЧтение и запись CSV-файлов в GolangРабота с cookie в GolangРегистры в GoКэширование данных в GolangПреобразование byte в string в GolangByte в GoИспользование bufio для работы с потоками данных в GolangДобавление данных и элементов (add) в Go
Логирование в Golang. Zap, Logrus, Loki, GrafanaРабота с Docker-контейнерами в GoИспользование pprof в GolangМеханизмы синхронизации в GolangРабота с пакетом S3 в GolangМониторинг Golang приложений с помощью PrometheusОптимизация проектов на GoПаттерны проектирования в GolangМиграции базы данных в GolangОркестрация контейнеров Go с Kubernetes + DockerGjGo Playground и компилятор GolangИспользование go mod init для создания модулей GolangРабота с переменными окружения (env) в GolangКоманда go build в GolangАвтоматизация Golang проектов — CI/CD с GitLab CI и JenkinsОтладка кода в GolangЧтение и использование конфигурации в приложениях на GolangКомпиляция в GolangКак развернуть Go-приложение на облаке AWSАутентификация в Golang
Сетевые протоколы в GoПеременные в GolangЗначения в GolangДженерик %T и его применение в GolangТипы данных в GolangИспользование tls в GolangИспользование tag в структурах GolangSwitch в GoСтроки в GolangРабота с потоками (stream) в GolangSelect в GoРуны в GoРабота с пакетом params в GolangКонвертация строк в числа в GolangNull, Nil, None, 0 в GoНаименования переменных, функций и структур в GoInt в GolangУстановка GolangЧтение и установка HTTP заголовков в GolangMethods в GolangGoLand — IDE для разработки на Golang от JetBrainsОбработка «not found» в GolangFloat в GolangФлаги командной строки в Go (Golang)Запуск внешних команд в GolangОбработка ошибок в GoИспользование defer в GolangЗначения default в GolangГенерация кода в GoФорматирование кода в GolangЧистая архитектура в GolangКаналы (channels) в GolangПолучение body из HTTP запроса в Golang
Открыть базу знаний