
`new(t)` 和 `&t{}` 都在运行时分配内存并返回指针,但语义和适用场景不同:前者仅支持零值初始化且适用于所有类型,后者支持自定义字段初始化且仅适用于可字面量化的复合类型(如 struct、slice、map 等)。
在 Go 中,new(T) 和 &T{} 确实都会为类型 T 分配堆内存(或由编译器优化到栈上),并返回指向该内存的 *T 指针。表面看功能重叠,但二者在语言设计、使用惯用法和实际约束上存在关键差异。
✅ 语义与能力对比
-
new(T):
- 总是返回指向零值的指针;
- 适用于任意类型:new(int)、new(string)、new([3]int)、new(func()) 均合法;
- 不支持字段初始化,无法指定初始值;
- 底层等价于声明一个匿名变量再取地址:var t T; return &t。
-
&T{...}(复合字面量取址):
- 仅适用于可构造字面量的类型:struct、array、slice、map、channel、func(需显式签名);
- 支持显式初始化:&struct{X, Y int}{1, 2}、&[]int{1,2,3}、&map[string]int{"a": 1};
- 若省略字段(即 &T{}),则各字段按零值初始化,效果 看似 等同 new(T),但底层实现和可读性更优。
// ✅ 合法且推荐:结构体带初始化
s := &struct{ Name string; Age int }{"Alice", 30}
// ✅ 合法:零值结构体(更清晰表达意图)
s0 := &struct{ Name string; Age int }{}
// ✅ 合法:基础类型只能用 new
pInt := new(int) // 等价于 var x int; pInt := &x
pFunc := new(func()) // 唯一可行方式获取 func 类型指针
// ❌ 编译错误:基础类型不支持字面量取址
// bad := &int{} // syntax error: cannot take address of int{}
// bad := &string{} // same⚠️ 实际使用建议
- 优先使用 &T{...}:对 struct、slice、map 等,它更直观、可读性强,且支持初始化,是 Go 社区公认的惯用写法(idiomatic Go)。
-
new(T) 使用场景极窄:
- 当你需要一个指向零值的指针,且 T 是基础类型(如 int、bool)或函数类型,而 &T{} 不可用时;
- 极少数泛型或反射场景中需统一类型构造逻辑(但通常有更优替代方案)。
-
多数情况下,应避免显式分配指针:
Go 编译器具备逃逸分析能力,能自动决定变量分配在栈还是堆。直接声明值再取址往往更简洁、安全:
// ? 不必要地强调“堆分配” p := new(int) *p = 42 // ? 更自然、更符合 Go 风格 x := 42 p := &x // 编译器自动决策 x 是否逃逸
? 总结
new(T) 并非冗余,而是为类型系统完整性所保留的底层原语;但它在日常开发中极少出现——Go 的设计哲学是“显式优于隐式,简洁优于繁琐”。当你看到 new(T),应首先思考:是否真的需要它?能否用 &T{...} 替代?或者干脆用值语义 + 编译器优化?答案通常是后者。掌握这一区别,有助于写出更地道、更易维护的 Go 代码。







