Введение: что мы создаём в Go
В Go есть два больших семейства типов:
Value types — обычные значения:
int,float64,bool,struct, массивы.Reference types — ссылочные структуры:
slice,map,chan.
Value types можно размещать где угодно: в стеке, в куче или внутри других объектов.
Reference types — это конструкции уровня рантайма с внутренними механизмами: простое объявление через var даст nil и вызовет панику при попытке использовать. Именно поэтому Go использует new и make по-разному.
new в Go: простое выделение памяти
Функция new(T) выделяет память под тип T, обнуляет её и возвращает указатель на T.
type Config struct {
Enabled bool
Count int
}
cfg := new(Config)
// cfg имеет тип *Config
// cfg.Enabled == false
// cfg.Count == 0
Внутри вызывается
runtime.newobject, который делаетmallocнужного размера и очищает память нулями.Подходит для value types: int, string, bool, struct, array.
new(T) vs &T{}
type User struct {
Name string
Age int
}
u1 := new(User) // выделяет объект в куче
u2 := &User{} // может быть в стеке или куче
Разница:
new(T)всегда аллоцирует в куче.&T{}может остаться в стеке, если компилятор считает это безопасным (escape analysis).
Используйте &T{} для высокоэффективного кода с минимальными аллокациями.
Когда new действительно нужен
Generic‑код: тип
Tнеизвестен на этапе компиляции.
func NewPointer[T any]() *T {
return new(T)
}
Явная аллокация в куче: иногда нужно гарантировать объект в heap.
pool := sync.Pool{
New: func() any {
return new(MyStruct)
},
}
Опциональные значения через nil:
type Options struct {
RetryCount *int
}
o := Options{}
o.RetryCount = new(int)
*o.RetryCount = 3
Ограничения new
new([]int)вернёт*[]intс nil — использовать как полноценный slice нельзя:
s := new([]int)
fmt.Println(*s == nil) // true
(*s)[0] = 1 // panic
Аналогично с
mapиchan.
make в Go: инициализация runtime‑структур
make не просто выделяет память. Он создаёт рабочие slice, map и chan с полностью инициализированными внутренними структурами.
Slice: указатель, длина, вместимость
s := make([]int, 10, 100)
Создаёт slice header:
type sliceHeader struct {
Data uintptr // указатель на массив
Len int
Cap int
}
Slice готов к использованию и может расширяться до
capбез новых аллокаций.var s []intдаст nil-слайс — любое обращение к элементу вызовет панику.
Map: работа с бакетами
m := make(map[string]int, 100)
Инициализирует внутреннюю структуру hmap:
count, flags, бакеты, старые бакеты для перестройки.
new(map[string]int)создаст только nil-указатель — использовать его нельзя.
Chan: синхронизация и буфер
c := make(chan int, 5)
Создаёт полноценную очередь сообщений с буфером и счетчиками.
var c chan intдаст nil-канал — операции блокируют навсегда.
Когда использовать make, а когда new
Сценарий | Выбор | Почему |
|---|---|---|
Slice, map, chan |
| Создаёт рабочую структуру, готовую к использованию |
Value type, generic |
| Получение указателя на zero-value |
Явная куча для value type |
| Гарантированное выделение в heap |
Опциональные значения |
| Возможность различать nil и заданное значение |
Вывод
new: простое выделение памяти и указатель на ноль.make: инициализация runtime‑структур, готовых к работе.
Понимание разницы между ними — обязательный минимум для любого разработчика на Go. Используйте make для slice, map и chan, а new — для value types и generic-кода.
Create an account or sign in to leave a review
There are no reviews to display.