логотип 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, когда требуется инициализация.

Карта развития разработчика

Получите полную карту развития разработчика по всем направлениям: frontend, backend, devops, mobile