最常用且易出错的是字面量初始化,需严格匹配字段顺序与大小写;new()和&User{}仅做零值分配;含默认值或校验时应使用New开头的自定义函数。

直接字面量初始化:最常用也最容易出错的方式
Go 中结构体初始化最直观的是用字面量,但必须注意字段顺序和是否导出。如果结构体字段未全部赋值,未指定的字段会按类型零值初始化(int 为 0,string 为 "",指针为 nil)。
- 字段名必须拼写完全一致,大小写敏感;
UserName和username是两个不同字段 - 如果结构体定义在其他包中,只有首字母大写的字段(即导出字段)才能被外部包通过字面量直接赋值
- 混合使用位置式和键值式会报错:
User{"tom", Age: 25}❌,必须全用键值式或全用位置式
type User struct {
Name string
Age int
}
u1 := User{"Alice", 30} // 位置式,依赖声明顺序
u2 := User{Name: "Bob", Age: 28} // 键值式,推荐,可读性强、抗字段增删使用 new() 和 &T{}:区别在于返回值类型和零值处理
new(User) 返回指向零值结构体的指针(*User),所有字段都是零值;&User{} 效果相同,但更常用、更直观。
-
new(User)和&User{}都不会调用任何构造逻辑,只是内存分配 + 零初始化 - 不要误以为
new()是“构造函数”——Go 没有构造函数概念 - 若结构体含非零默认值字段(比如希望
Status默认是"active"),这两种方式都做不到,必须封装成函数
uPtr1 := new(User) // *User,Name="", Age=0
uPtr2 := &User{} // 等价于上面,但更常见
uPtr3 := &User{Name: "Cindy"} // 字段可部分初始化,未写的仍是零值自定义初始化函数:应对默认值、校验或依赖注入
当结构体需要非零默认值、字段校验、或初始化时需访问外部资源(如配置、DB 连接),应封装为函数,通常命名为 NewXXX。
- 函数名用
New开头表示它返回一个新实例(Go 社区惯例) - 接收参数应尽量精简,避免把所有字段都塞进参数列表;可用选项模式(Option Pattern)提升扩展性
- 不要在初始化函数里做重操作(如网络请求),除非明确设计为“懒加载”或“必须初始化完成”
func NewUser(name string, age int) *User {
if name == "" {
name = "anonymous"
}
if age < 0 {
age = 0
}
return &User{Name: name, Age: age}
}
// 调用
u := NewUser("David", -5) // → Name="David", Age=0
嵌套结构体与匿名字段:初始化时容易漏掉层级或混淆字段归属
嵌套结构体初始化时,键值式写法必须显式写出每一层字段名;匿名字段(内嵌)则允许“提升”访问,但初始化仍需按实际结构展开。
立即学习“go语言免费学习笔记(深入)”;
- 匿名字段不等于自动继承;初始化时仍要写完整路径:
Profile: Profile{Nick: "X"},不能简写为Nick: "X" - 如果嵌套结构体本身有导出字段,且你用字面量初始化外层结构体,内层字段必须可导出才能被赋值
- 使用
json.Unmarshal或其他序列化库时,嵌套字段标签(如json:"nick")不影响初始化语法,只影响编解码行为
type Profile struct {
Nick string
}
type User struct {
Name string
Profile // 匿名字段
}
// 正确初始化(显式写出嵌套)
u := User{
Name: "Eve",
Profile: Profile{Nick: "evie"},
}
// 错误:不能直接写 Nick(Go 不支持字段提升式初始化)
// u := User{Name: "Eve", Nick: "evie"} ❌
字段多、嵌套深、有默认逻辑的结构体,靠裸字面量初始化很快会失控。真正工程中,90% 的非 trivial 结构体都应该配一个 NewXXX 函数——不是为了“规范”,而是为了把默认值、约束、上下文耦合点收口到一处。









