多数时候该选 &t{},因为它支持字段初始化、嵌入结构体、调用指针方法,语义清晰;new(t) 仅分配零值内存且无法初始化字段,适用场景极少。

什么时候用 &T{} 更安全
多数时候该选 &T{},尤其当你需要初始化字段值、嵌入结构体或调用带接收者的方法。它直接构造一个零值结构体再取地址,语义清晰,且能配合字段名赋值。
常见错误现象:new(T) 返回的指针指向全零值内存,但无法在初始化时设字段——比如想让 Name 字段为 "default",new(MyStruct) 做不到,必须后续手动赋值,容易漏。
- 支持字段初始化:
&MyStruct{Name: "foo", Age: 42} - 可嵌入匿名字段并触发其初始化逻辑(如
sync.Mutex的零值是可用的) - 与方法集一致:如果方法定义在
*T上,&T{}直接获得可调用实例
为什么 new(T) 几乎没优势
new(T) 只做一件事:分配 T 类型大小的零值内存,返回 *T。它不支持任何初始化语法,也不参与类型推导——连类型都要显式写两次。
使用场景极少:仅当你要一个纯零值指针、且明确不想写结构体字面量时才可能用到,比如某些泛型工具函数内部临时分配,但实际项目中几乎见不到合理用例。
立即学习“go语言免费学习笔记(深入)”;
- 不能初始化字段:
new(MyStruct).Name = "x"是运行时赋值,不是初始化 - 类型冗余:
new(string)不如&""或直接用字符串字面量 - 和
make混淆:新手常误以为new也用于 slice/map/channel,但它完全不支持这些类型,会编译报错cannot use new(...) (value of type *map[T]U) as map[T]U value
结构体含非零值字段时的坑
如果结构体里有指针、slice、map、channel 或 interface 字段,&T{} 和 new(T) 都只给它们赋零值(nil),不会自动 make 或 new 子成员。这是最容易被忽略的点。
例如:type Config struct { Data map[string]int },&Config{} 得到的是 Data == nil,后续若直接 c.Data["k"] = 1 会 panic。
- 必须显式初始化:
&Config{Data: make(map[string]int)} - 嵌入 sync 包类型时安全:因为
sync.Mutex{}零值本身合法,无需额外new - 不要依赖
new(T)“更底层”:它和&T{}在内存分配行为上无实质差异,都是堆上分配(逃逸分析决定)
性能与逃逸分析其实没差别
别指望换写法能“避免逃逸”或“提升性能”。Go 编译器对 &T{} 和 new(T) 的逃逸分析结果通常一致——只要结构体大小不确定、或生命周期超出当前函数,就会逃逸到堆。
实测过几十种组合,包括小结构体、含 slice 的结构体、带方法的结构体,两者生成的汇编和逃逸提示(go build -gcflags="-m")完全一样。
- 真正影响逃逸的是使用方式,不是初始化语法:比如把返回的
*T存进全局变量、传给 goroutine、或作为返回值暴露出去 -
new(T)多一次类型推导开销?不存在。这是编译期行为,零成本 - 唯一可测差异:
&T{}多几个字符,但换来可读性和可控性,很划算
&T{} 时顺手补上 make,比纠结 new 有意义得多。










