Go中结构体字段首字母大小写决定导出性:大写可跨包访问,小写仅本包可见;JSON和ORM默认忽略小写字段;初始化须用键值对;方法接收者选*T才能修改字段;嵌入结构体非继承而是字段提升。

结构体字段首字母大小写决定能否跨包访问
Go 没有 public/private 关键字,字段是否能被其他包引用,只看首字母:大写(如 Name、ID)可导出,小写(如 name、id)仅限本包内使用。
常见错误现象:cannot refer to unexported field name —— 跨包调用时直接编译失败;json.Marshal 输出为空字段或漏掉整个字段;GORM 插入数据库时对应列始终为 NULL 或被跳过。
- JSON 序列化默认忽略所有小写字段,哪怕你手动传了值也不会出现在输出里
- GORM 等 ORM 库同样只扫描大写字段做映射,
CreatedAt会自动填充,createdAt则完全静默丢弃 - 嵌入结构体时,若嵌入的是小写类型(如
person),其字段不会被提升,s.name编译不通过
初始化结构体必须显式命名字段,别依赖顺序
用 Person{"Alice", 30} 这种位置式初始化,一旦结构体字段增删或顺序调整,就会 silently 错位:比如第 3 个字段本该是 City,但你少写一个,30 就可能被塞进 Age 后面的 Score 字段,运行时逻辑错乱,编译器还不报错。
推荐始终用键值对:Person{Name: "Alice", Age: 30}。
立即学习“go语言免费学习笔记(深入)”;
- 字段可乱序、可省略(省略字段取零值:
""、0、nil) - 加新字段后,旧初始化代码仍能编译通过,不会意外覆盖其他字段
- 嵌套结构体也必须完整初始化:
Address: Address{City: "Beijing"},不能简写为Address: {"Beijing"}
方法接收者选 *T 还是 T 不是风格问题
值接收者(func (p Person) Grow())会拷贝整个结构体;指针接收者(func (p *Person) Grow())只传地址。两者行为和性能差异显著。
- 只要方法内要修改字段(哪怕只是
p.Age++),就必须用指针接收者——值接收者改的是副本,原结构体完全不变 - 结构体含
[]byte、map、chan或字段较多时,值接收者会引发明显内存拷贝开销 - 如果任一方法用了指针接收者,建议全部统一用指针,避免调用方混用
s.Method()和&s.Method()导致语义混乱 - 小写字段即使在指针接收者方法里也无法被赋值——因为不可寻址,连编译都过不去
嵌入结构体不是继承,是字段提升 + 方法组合
type Employee struct { Person; Title string } 看起来像“继承”,实际只是把 Person 的导出字段和方法“提上来”,让 e.Name 和 e.SayHello() 可以直接调用,没有虚函数表,也没有 is-a 关系。
- 两个嵌入结构体若有同名方法(如都定义了
Speak()),e.Speak()会编译报错:ambiguous selector,必须显式写成e.Person.Speak() - 嵌入的结构体类型本身必须可导出(首字母大写),否则字段不会被提升,
e.Name无法访问 - 嵌入结构体字段若为小写(如
person),它本身不可被外部包访问,更不会参与字段提升


















