Для каких типов используется функция `make` и в чем её отличие от `new`?

Ответ

Встроенная функция make используется для инициализации и выделения памяти только для трёх встроенных типов:

  • Срезы (slices): make([]T, len, cap) Создаёт срез, выделяя память под базовый массив и инициализируя дескриптор среза (указатель, длину и ёмкость).

    s := make([]int, 5, 10) // len=5, cap=10
  • Карты (maps): make(map[K]V, initialCapacity) Создаёт и инициализирует хэш-таблицу для хранения пар ключ-значение.

    m := make(map[string]int, 100)
  • Каналы (channels): make(chan T, bufferSize) Создаёт канал для коммуникации между горутинами и, если указано, выделяет буфер.

    ch := make(chan int, 10)

Ключевое отличие make от new

Характеристика make(T, ...) new(T)
Назначение Инициализация срезов, карт и каналов Выделение памяти для любого типа
Возвращаемое значение Значение типа T (например, []int) Указатель на тип *T (например, *int)
Результат Возвращает инициализированный, готовый к использованию объект Возвращает указатель на обнулённую (zeroed) область памяти

Итог: make создаёт и настраивает сложные внутренние структуры (хэш-таблицы, буферы), а new просто выделяет "сырую" обнулённую память и возвращает указатель на неё.

Ответ 18+ 🔞

А, блядь, вот эта тема про make и new — классика, вечный источник путаницы для новичков, которые только начинают в Go лезть. Слушай, сейчас разжуёшь, как кот сука собака.

Короче, make — это не какой-то универсальный инструмент, ёпта. Это спецсредство, как отмычка для трёх конкретных замков. И замки эти — срезы, карты и каналы. Всё! Больше ни на что он не способен, пизда с ушами.

Зачем он? А затем, что эти три штуки — они не простые, у них внутри сложная механика, им перед работой движок завести надо. make как раз этот движок и заводит: память выделяет, внутренние структуры инициализирует, чтоб сразу в дело можно было.

Вот смотри, как это выглядит вживую:

  • Срез (slice): make([]int, 5, 10) Тут он тебе не просто память под массив выделит. Он создаст этот массив на 10 ячеек, потом сделает дескриптор среза (указатель, длину, ёмкость), нацелит его на массив и скажет: «Вот, длина у тебя пока 5, но можешь расти до 10, не парься». Готовый к работе инструмент, а не просто кусок памяти.

  • Карта (map): make(map[string]int, 100) Тут вообще магия. Он не просто ящик под пары ключ-значение выделит. Он построит целую хеш-таблицу, ёбушки-воробушки, со всеми бакетами и алгоритмами поиска. Указание 100 — это намёк системе: «Готовься, я планирую где-то столько пар пихать, не тормози потом».

  • Канал (channel): make(chan int, 10) А это вообще для межгорутинного общения. Он создаёт эту трубу для данных и, если надо, буфер к ней прикручивает на 10 сообщений. Без make канал — это просто концепция, а с ним — уже работающий механизм синхронизации.

А в чём, блядь, разница с new? Вот тут собака зарыта!

Это два разных зверя, а их постоянно путают. Смотри таблицу, чтобы в голове не каша была:

Признак make(T, ...) new(T)
Что делает? Создаёт и настраивает срезы, карты, каналы. Выделяет обнулённую память для чего угодно и даёт на неё указатель.
Что вернёт? Готовый объект типа T (например, []int). Указатель *T на пустую память (например, *int).
Состояние? Инициализирован, можно сразу юзать. Zero-value (нули, nil). Карта или канал от new — бесполезны.

Проще говоря: new — это как получить пустой картонный ящик от завода. make — это как привезти из IKEA шкаф, но уже собранный, с полочками и дверцами на петлях, осталось только вещи разложить.

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