new()仅清零内存而不初始化结构,返回nil指针;make()才真正初始化slice/map/chan的运行时数据结构,二者不可互换。

new() 只做内存清零,不初始化类型内部结构
当你写 new([]int) 或 new(map[string]int),Go 确实会分配一块足够存下该类型零值的内存,并把整块内存置为 0 —— 但这就完了。它不会调用任何构造逻辑,也不会让底层数据结构(比如 slice 的底层数组、map 的哈希表)真正可用。
常见错误现象:panic: assignment to entry in nil map 或 index out of range,就因为误以为 new(map[string]int) 返回了一个能直接用的 map。
-
new(T)返回*T,T 必须是具体类型,不能是接口或未定义类型 - 对复合类型(slice/map/chan/func)用
new(),得到的是 nil 指针,不能直接赋值或调用方法 - 性能上几乎无开销,但它返回的值多数时候不能直接用,属于“半成品”
make() 才真正初始化运行时数据结构
make() 是专为 slice、map、chan 三类类型设计的内置函数,它不仅分配内存,还调用运行时初始化逻辑:比如为 map 分配 hash 表桶、为 slice 设置 len/cap 字段并关联底层数组、为 chan 创建等待队列和锁。
使用场景:只要你要立刻往里面塞数据、遍历、发送接收,就必须用 make()。比如 make([]int, 5) 返回一个长度为 5 的切片,底层数组已就位;make(map[string]int) 返回一个可立即 m["k"] = 1 的 map。
立即学习“go语言免费学习笔记(深入)”;
-
make([]T, len)和make([]T, len, cap)参数顺序固定,cap 可选但必须 >= len -
make(map[K]V)不接受容量参数;make(chan T)可带缓冲大小,如make(chan int, 10) - 对非这三类类型(如 struct、*int)调用
make()会编译报错:cannot make type T
底层分配器视角:new() 走 mallocgc,make() 可能触发 runtime.mapassign 等
从 Go 内存分配器角度看,new(T) 最终调用的是 mallocgc(size, typ, needzero),只负责堆上分配 + 清零;而 make(map[K]V) 会进一步调用 runtime.makemap,后者根据 key/value 类型计算哈希参数、预分配桶数组、初始化 hmap 结构体字段——这才是 map 能用的根本。
容易踩的坑:有人试图用 new() 配合强制类型转换绕过 make(),比如 (*map[string]int)(unsafe.Pointer(new([8]byte))),这在 GC 扫描时会崩溃,因为运行时无法识别伪造的 map 头部。
- 所有
make()初始化的结构,其指针都由 GC 可达性跟踪;new()返回的指针也受 GC 管理,但内容若不含指针字段,GC 不会递归扫描 - 小对象(new() 和
make()都不暴露这个细节,用户无需干预 -
go tool trace 里能看到
makemap、makeslice等独立事件,但new()通常合并进普通 malloc 跟踪点,说明它更轻量
什么时候该用哪个?看返回值能不能直接用
一句话判断:如果变量声明后第一行就要赋值、append、range、send/receive,就用 make();如果只是需要一个指向零值的指针(比如传参给函数要求 *T),且后续会用 &struct{} 或其他方式填充,那 new() 更直白。
典型反例:var m map[string]int = new(map[string]int) —— 这等价于 m = nil,不是你想要的空 map。
- 要空 slice → 用
make([]int, 0),别用new([]int) - 要空 map → 用
make(map[string]int),别用new(map[string]int) - 要新 struct 指针 →
new(MyStruct)和&MyStruct{}效果一致,后者更常见 - chan 同理:必须
make(chan int),new(chan int)得到的是*chan int,根本没法 send
最易被忽略的一点:make() 的三个支持类型是硬编码在编译器里的,连 reflect 包都不能模拟它的行为 —— 你没法用反射“重新实现” make(map)。这意味着任何想绕过它的动态初始化尝试,基本都会掉进运行时兼容性或 GC 安全的坑里。










