结构体基础
结构体是值类型,用于把一组字段组合成一个复合类型。
type User struct { |
声明与初始化
// 零值:所有字段取各自零值 |
注意:
- 字段名大写:可导出,跨包可访问。
- 字段名小写:包内私有。
- 结构体赋值/传参是浅拷贝。如果字段包含
slice、map、指针,复制的是引用地址。
type Data struct { |
结构体指针与函数传参
结构体较大时通常用指针,避免复制。
func update(u *User) { |
指针字段可以为 nil,访问前要判断:
type User struct { |
匿名结构体
不需要提前定义类型,适合临时数据、测试、JSON 构造。
p := struct { |
匿名结构体类型由字段完全决定,字段相同则类型相同;但通常不建议滥用。
空结构体 struct{}
struct{} 不占空间,常见用途:
// 集合 |
结构体方法
Go 没有类,但可以给结构体定义方法。
type Counter struct { |
使用:
c := Counter{} |
选择建议:
- 需要修改结构体:用指针接收者。
- 结构体较大:用指针接收者。
- 需要实现接口且方法集要求:注意
T和*T的方法集不同。 - 小结构体只读:可用值接收者。
方法集规则简记:
- 类型
T的方法集包含值接收者方法。 - 类型
*T的方法集包含值接收者和指针接收者方法。 - 所以
*T通常能实现更多接口。
type Incrementer interface { |
嵌套结构体:命名字段
嵌套结构体就是把一个结构体作为另一个结构体的命名字段。
type Address struct { |
初始化:
u := User{ |
访问:
fmt.Println(u.Addr.City) |
特点:
- 必须通过字段名
u.Addr访问。 - 内层结构体的字段不会自动提升到外层。
- 适合表达“拥有一个”的关系,如用户拥有地址。
嵌套指针:
type User struct { |
匿名嵌入:字段提升与组合
匿名嵌入是把类型名直接写在结构体中,不写字段名:
type Address struct { |
初始化:
u := User{ |
访问提升字段:
fmt.Println(u.City) // 提升访问,等价 u.Address.City |
嵌入的方法也会被提升:
func (a Address) Full() string { |
嵌入指针
type User struct { |
坑:嵌入指针可能为 nil,访问提升字段会 panic。
var u User |
字段冲突
如果外层和内层有同名字段,外层优先:
type Address struct { |
如果嵌入多个类型且有同名字段,同层冲突必须显式指定:
type A struct{ Name string } |
嵌入接口
结构体可以嵌入接口,常用于装饰器、代理、测试 mock:
type Reader interface { |
嵌套 vs 嵌入
| 写法 | 示例 | 访问 | 方法提升 | 语义 |
|---|---|---|---|---|
| 命名字段嵌套 | Addr Address |
u.Addr.City |
不提升 | 拥有一个 Address |
| 匿名嵌入 | Address |
u.City 或 u.Address.City |
提升 | 组合/复用能力 |
示例对比:
// 嵌套 |
推荐:
- 表达“有一个组件”用命名字段嵌套。
- 表达“组合能力、方法提升、接口复用”用匿名嵌入。
- 不要为了少写
.Addr而滥用嵌入,嵌入会增加 API 面。
结构体标签与 JSON
结构体标签常用于序列化、ORM、校验等。
type User struct { |
b, _ := json.Marshal(User{ID: 1, Name: "", Password: "123"}) |
嵌套结构体 JSON:
type User struct { |
匿名嵌入 JSON 默认会扁平化提升字段:
type User struct { |
可通过标签控制:
type User struct { |
反射读取标签:
t := reflect.TypeOf(User{}) |
结构体比较、拷贝与 map key
如果所有字段都可比较,结构体可用 == 比较:
type Point struct { |
包含 slice、map、func 等不可比较字段时,不能用 ==:
type Data struct { |
需要深比较可用 reflect.DeepEqual,但注意性能和语义。
可比较结构体可作为 map key:
m := map[Point]string{ |
构造函数
Go 没有构造函数,通常用 NewXxx:
type User struct { |
可做默认值、校验、初始化内部 map:
type Cache struct { |
常见坑与建议
- 结构体是值类型:赋值和传参默认复制。
- 浅拷贝:含 slice/map/指针字段时共享底层数据。
- 方法接收者选择:需要修改或大对象用指针接收者。
- 嵌入指针可能 nil:访问提升字段前要判断。
- 字段冲突要显式访问:
c.A.Name。 - 跨包只能访问导出字段:字段名首字母大写。
- 不能包含自身值类型字段,但可包含
*Self:type Node struct {
Next *Node
} - 字段顺序影响内存对齐,极端性能场景可调整字段顺序,一般业务不必过早优化。
- 匿名嵌入不是继承:只是字段和方法提升,本质是组合。
- 结构体字面量位置初始化易错:字段增删会破坏代码,推荐键值初始化。
Go 结构体的核心用法包括:定义与初始化、值/指针语义、方法、匿名结构体、空结构体、标签、JSON、比较、map key、构造函数。嵌套结构体分为两类:
- 命名字段嵌套:
Addr Address,访问u.Addr.City,不提升字段和方法,语义清晰。 - 匿名嵌入:
Address,可u.City访问并提升方法,适合组合和接口复用,但要注意冲突和 nil 指针。