← Назад к вопросам

Что возвращает New?

1.0 Junior🔥 281 комментариев
#Основы Go

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI7 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Отличный вопрос! Он затрагивает одну из важнейших концепций в Go — работу с указателями и конструкторами.

Краткий ответ

Функция New из пакета builtin возвращает указатель на вновь выделенную (аллоцированную) нулевую (zero) память для переданного типа. Результат имеет тип *T, где T — аргумент функции.

Проще говоря, new(T) возвращает указатель на новую переменную типа T, инициализированную нулевым значением для этого типа (0 для чисел, false для bool, "" для string, nil для указателей, срезов, карт, каналов и функций).

Подробное объяснение с примерами

Функция new — это встроенная (built-in) функция, доступная без импорта каких-либо пакетов. Её сигнатура выглядит так:

func new(Type) *Type

Давайте разберем её работу на практике.

1. Возврат указателя на нулевое значение

package main

import "fmt"

type MyStruct struct {
    ID    int
    Name  string
    Active bool
}

func main() {
    // new возвращает *int - указатель на int, инициализированный нулём (0)
    ptrInt := new(int)
    fmt.Printf("Тип: %T, Значение: %v, Разыменованное: %d\n", ptrInt, ptrInt, *ptrInt) // *int, 0x..., 0

    // new возвращает *MyStruct - указатель на структуру, все поля которой zero-valued
    ptrStruct := new(MyStruct)
    fmt.Printf("Тип: %T, ID: %d, Name: '%s', Active: %v\n", ptrStruct, ptrStruct.ID, ptrStruct.Name, ptrStruct.Active)
    // *main.MyStruct, ID: 0, Name: '', Active: false

    // Это эквивалентно следующему:
    var temp MyStruct
    ptrStruct2 := &temp // Взятие адреса локальной переменной
    // Однако 'new' выполняет выделение памяти напрямую, без явного создания переменной.
}

2. Сравнение new с составными литералами (&T{})

Часто new путают или сравнивают с использованием составного литерала с взятием адреса. Это разные операции с важными нюансами.

package main

import "fmt"

func main() {
    // Использование new - нулевая инициализация
    ptrViaNew := new(int)
    fmt.Println(*ptrViaNew) // 0

    // Использование литерала с адресом - МОЖЕТ содержать инициализацию
    x := 42
    ptrViaAddr := &x
    fmt.Println(*ptrViaAddr) // 42

    // Для структур разница более наглядна
    type Point struct{ X, Y int }

    p1 := new(Point)          // X=0, Y=0
    p2 := &Point{}            // Тоже X=0, Y=0 - Эквивалентно new(Point) в этом случае
    p3 := &Point{X: 10, Y: 20} // Явная инициализация! Это то, чего нельзя сделать с 'new'.

    fmt.Println(p1, p2, p3) // &{0 0}, &{0 \%0}, &{10 20}
}

Ключевое отличие: new(T) всегда инициализирует память нулевым значением. &T{...} позволяет выполнить не нулевую инициализацию, указав значения для полей.

3. Что именно выделяет new?

Важно понять, что new выделяет память в куче (heap). Возвращаемый указатель ссылается на область памяти, жизнь которой управляется сборщиком мусора (garbage collector). Это значит, что переменная "переживает" выход из функции, в которой был вызван new.

package main

import "fmt"

func createPointer() *int {
    // Локальная переменная 'x' исчезнет после возврата из функции.
    // x := 42
    // return &x // Так тоже можно, но под капотом компилятор может "сбежать" x в кучу.

    // new явно аллоцирует в куче. Память будет жить, пока на неё есть ссылка.
    ptr := new(int)
    *ptr = 42 // Присваиваем значение уже после выделения.
    return ptr // Это безопасно.
}

4. Практическое применение и рекомендации

На практике new используется довольно редко по сравнению с составными литералами. Вот почему:

  • Для структур: &MyStruct{Field: value} читается лучше и является идиоматичным способом.
  • Для базовых типов: Проще объявить переменную и взять её адрес (x := 0; ptr := &x), если не нужно именно нулевое значение.
  • new полезен в некоторых специфических случаях:
    *   Когда нужен **именно указатель на нулевое значение** как сигнал или sentinel value.
    *   Внутри низкоуровневых функций или при работе с **`unsafe.Pointer`**, где требуется чистое выделение памяти.
    *   В старом коде или в определенных шаблонах (хотя даже там сейчас часто предпочитают литералы).

Итог

АспектЧто возвращает new(T)?
ТипУказатель *T
Значение в памятиНулевое значение типа T (zero value)
Место аллокацииКуча (Heap)
Основная альтернативаИспользование составного литерала с адресом: &T{...}
ИдиоматичностьИспользуется реже, чем &T{}. Предпочтение отдается явной инициализации.

Таким образом, вызов new(int) возвращает *int (указатель на int), и разыменование этого указателя даст 0. Это фундаментальный механизм языка для динамического выделения памяти под указатель.