В чем разница между функциями new и make в Go
Что проверяют
Понимаете ли вы, чем в Go выделение памяти отличается от инициализации внутренних структур, и знаете ли нулевые значения типов. Следом часто просят объяснить, почему запись в var m map[string]int паникует, а срез из new([]int) бесполезен.
Как отвечать
Коротко: new выделяет память под любой тип и возвращает указатель на нулевое значение, make создаёт и инициализирует только срез, мапу или канал и возвращает само значение.
new(T): работает с любым типом, возвращает*T, память обнулена.new(int)даёт указатель на0,new(User)эквивалентно&User{}. Начиная с Go 1.26 вnewможно передать выражение:new(int64(300))сразу вернёт указатель на это значение, что удобно для необязательных полей в JSON и protobuf;make(T, args): только дляslice,map,chan. Для среза задаёт длину и ёмкость,make([]int, 0, 100); для мапы подсказку размера,make(map[string]int, 1000); для канала размер буфера,make(chan int, 10);- почему два механизма: срез, мапа и канал это заголовки над скрытыми структурами. Их нулевое значение
nil: из nil-мапы можно читать, но запись паникует, отправка в nil-канал блокирует навсегда.makeстроит внутреннюю структуру, аnewотдал бы указатель на тот жеnil.
Хорошо упомянуть, что на практике new встречается редко: композитный литерал &T{} читается лучше. А куда попадёт память, на стек или в кучу, решает escape-анализ компилятора, а не выбор между new и make.
Где спотыкаются
Говорят, что new «не инициализирует» память. Инициализирует, нулевым значением. Не создаёт он только внутренние структуры среза, мапы и канала.
Считают, что new всегда кладёт объект в кучу, а обычная переменная живёт на стеке. Это решает escape-анализ, посмотреть его решения можно через go build -gcflags=-m.
Путают длину и ёмкость: после make([]int, 5) вызов append добавляет элемент шестым, после пяти нулей, хотя человек ждал срез из одного элемента. Для заполнения через append нужен make([]int, 0, 5).