Go结构体非类,无继承重载,靠首字母大小写控制字段导出性;嵌入实现组合提升,值/指针接收影响方法修改;零值确定,初始化须按序或用字段名。

Go 语言中结构体(struct)不是类,不支持继承、方法重载或隐藏字段,但它是组合与封装的核心载体。定义和访问字段看似简单,实际容易在导出控制、嵌入、零值初始化和指针接收上出错。
如何正确定义 struct 并控制字段可见性
字段首字母大写才可被其他包访问;小写字母开头即为包私有。没有 public/private 关键字,全靠命名约定。
常见错误:误以为加了 json:"name" 标签就能让未导出字段被外部序列化——不行,encoding/json 只能访问导出字段。
- 导出字段:
Name、ID、CreatedAt - 未导出字段:
cache、version、isValid - 嵌入匿名字段时,其导出性决定是否“提升”到外层结构体
type User struct {
ID int `json:"id"`
Name string `json:"name"`
email string `json:"email"` // 小写 → 包内可用,JSON 不会序列化
}
struct 字段访问:值 vs 指针接收的差异
访问字段本身无区别,但调用方法时,值接收者会复制整个 struct,指针接收者才真正修改原值。字段赋值不受影响,但字段是 slice/map/chan/interface 时需注意底层引用共享。
立即学习“go语言免费学习笔记(深入)”;
常见错误:对值接收者方法修改字段,却发现调用后原变量没变;或误以为 u.Name = "x" 需要取地址才能生效——不需要,只要字段可写即可。
-
u.Name = "Alice"合法,u是变量且Name导出 -
users[0].ID = 100合法,即使users是切片 - 若
u是*User,u.Name仍可直接写,Go 自动解引用
嵌入 struct(anonymous field)时字段访问的隐式提升
嵌入非命名字段(如 time.Time 或 Logger)后,其导出字段和方法会“提升”到外层 struct,可直接访问。但提升不等于继承,没有多态,也没有字段冲突自动覆盖机制。
常见错误:两个嵌入 struct 有同名导出字段(如都含 ID),编译报错 “ambiguous selector”;或误以为嵌入后能用子类型赋值给父类型变量——Go 没有子类型关系。
- 嵌入
time.Time后,可直接写event.Before(...)和event.Year() - 嵌入
*sync.Mutex后,可直接调用event.Lock() - 若想避免提升,显式命名字段:
mu sync.Mutex,则必须写e.mu.Lock()
type Event struct {
time.Time
Title string
}
e := Event{Time: time.Now(), Title: "Launch"}
fmt.Println(e.Year()) // OK — Year() 被提升
零值、字段初始化与 struct 字面量写法
struct 零值是每个字段的零值组合(0、""、nil)。字段初始化必须按定义顺序或使用字段名,混用会导致编译错误。未指定字段将取零值,不会跳过。
常见错误:用位置参数初始化时漏掉中间某个字段,导致后续所有字段错位;或误以为未赋值字段会保持“未初始化”状态——它一定有明确零值。
- 安全写法始终用字段名:
User{Name: "Bob", ID: 42} - 如果结构体字段少且顺序稳定,位置写法可读:
Point{1, 2} - 嵌套 struct 初始化时,内部也建议用字段名,避免嵌套层级深导致歧义
最容易被忽略的是:嵌入 struct 的零值行为不透明——比如嵌入 sync.Mutex,它的零值本身就是有效可锁的;但嵌入自定义 struct 时,若其字段含指针或 map,零值可能引发 panic(如对 nil map 写入)。这时候得靠构造函数或显式初始化。










