应优先使用 &T{Name: "x"} 初始化结构体指针,它语义清晰、支持字段名赋值、不依赖顺序;new(T) 冗长且无法设字段值;含 map/slice/指针等字段需显式初始化,否则运行时 panic;复杂逻辑或未导出字段必须用 NewXXX() 工厂函数。

用 &T{} 初始化结构体指针最直接
绝大多数时候,你该写 &User{Name: "Alice", Age: 30},而不是 new(User) 或 User{}。它返回指针、支持字段名赋值、不依赖字段顺序,语义一目了然。
常见错误是写成 &User{"Alice", 30}——字段顺序一变就编译失败;加个未导出字段或调整定义顺序,立刻报 too few values in struct literal。
-
&User{Name: "Alice"}安全:没写的字段自动为零值(Age是 0,addr是"") -
new(User)不推荐:效果等价但冗长,且无法填字段值,纯属历史遗留 - 别对字面量裸取地址:
&struct{X int}{1}合法,但&{1}非法;&"hello"或&42直接编译报错
含 map/slice/chan/指针字段必须显式初始化
&T{} 只做零值分配,不会帮你 make 或 new 嵌套资源。字段默认是 nil,直接用就会 panic。
比如 cache map[string]int 在 &Config{} 里仍是 nil,后续 cache["k"] = v 触发 panic: assignment to entry in nil map。
立即学习“go语言免费学习笔记(深入)”;
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
cache: make(map[string]int, 16)items: make([]string, 0, 8)-
lock: &sync.RWMutex{}(不是new(sync.RWMutex),后者也行但不惯着) -
db *sql.DB这类外部资源字段,根本没法在字面量里填,得靠工厂函数注入
嵌套结构体指针初始化容易漏掉 &
写 A{B: &B{C: &C{}}} 时,少一个 & 或 & 和 { 中间换行/加空格,编译就挂。Go 要求 & 必须紧贴 {,不能有空格或换行。
更糟的是,JSON 反序列化或 mapstructure.Decode 进来时,*Profile 字段哪怕 JSON 有数据,默认仍是 nil——解码器不会自动 new 一个 Profile 给你。
- 深层嵌套建议拆成变量:
profile := &Profile{Name: "A"}; u := &User{Profile: profile} - 或统一走
NewProfile()、NewUser(),避免手写漏& - 切片元素是指针时(如
[]*User),字面量里每个元素都得带&:[]*User{&User{Name: "A"}},[]*User{{Name: "A"}}编译失败
什么时候必须用 NewXXX() 工厂函数
当结构体初始化逻辑超出了字面量能力,New 函数就不是“可选”,而是必需。
典型场景:字段要校验(Name != "")、需默认值(CreatedAt: time.Now())、含未导出字段(小写开头)、或要预分配资源(cache: make(...))。
- 包外用户无法用字面量初始化含小写字段的结构体,
NewUser()是唯一出口 func NewUser(name string) *User { return &User{Name: name, createdAt: time.Now()} }- 嵌套多层时,
NewConfig()→NewInstance()→NewService()分层封装,比一行嵌套字面量可读、可维护得多
真正容易被忽略的不是语法,而是“零值不等于可用值”——&T{} 给你一个指针,但里面所有 map、slice、*T 仍是 nil,不显式处理,运行时立刻崩。别指望编译器提醒你漏了 make。

















