引入

map 能用键存取数据,但有时候想把名字、年龄、地址这些不同类型的数据组合成一个整体。Go 的结构体就是用来做这件事的,把一组相关数据打包成一个自定义类型。

正文

定义

定义

结构体用来把一组相关数据组合在一起,是一个自定义类型。结构体由多个字段组成,每个字段有名字和类型。结构体是值类型,赋值和传参会复制整个结构体。

语法

// 定义结构体类型
type Person struct {
    Name string
    Age  int
}
 
// 创建实例
p := Person{Name: "Tom", Age: 20}

例子

定义和初始化

type User struct {
    Name  string
    Age   int
    Email string
}
 
// 按字段名初始化(推荐)
u1 := User{Name: "Tom", Age: 20, Email: "[email protected]"}
 
// 按顺序初始化(不推荐,字段顺序变了就出错)
u2 := User{"Amy", 22, "[email protected]"}
 
// 部分初始化,其余为零值
u3 := User{Name: "Bob"}
fmt.Println(u3) // {Bob 0 }

按字段名初始化最安全,不用管顺序。

零值

var u User
fmt.Println(u) // {  0 }

结构体的零值是每个字段取各自的零值。

访问和修改字段

u := User{Name: "Tom", Age: 20}
 
fmt.Println(u.Name) // Tom
fmt.Println(u.Age)  // 20
 
u.Age = 25
fmt.Println(u.Age)  // 25

. 访问和修改字段。

结构体指针

u := User{Name: "Tom", Age: 20}
p := &u
 
fmt.Println(p.Name) // Tom(自动解引用)
p.Age = 30
fmt.Println(u.Age)  // 30

结构体指针访问字段用 .,Go 自动解引用,不需要写 (*p).Name

嵌套结构体

type Address struct {
    City string
    Zip  string
}
 
type Person struct {
    Name    string
    Address Address
}
 
p := Person{
    Name:    "Tom",
    Address: Address{City: "Beijing", Zip: "100000"},
}
 
fmt.Println(p.Address.City) // Beijing

结构体的字段可以是另一个结构体,用 . 逐层访问。

匿名字段(嵌入)

type Animal struct {
    Name string
}
 
type Dog struct {
    Animal
    Breed string
}
 
d := Dog{
    Animal: Animal{Name: "Buddy"},
    Breed:  "Labrador",
}
 
fmt.Println(d.Name)       // Buddy(直接访问嵌入字段)
fmt.Println(d.Animal.Name) // Buddy(完整路径也行)

匿名字段没有字段名,外部结构体可以直接访问内部结构体的字段,像继承一样。

匿名字段的方法提升

type Animal struct {
    Name string
}
 
func (a Animal) Speak() string {
    return a.Name + " makes a sound"
}
 
type Dog struct {
    Animal
}
 
d := Dog{Animal: Animal{Name: "Buddy"}}
fmt.Println(d.Speak()) // Buddy makes a sound

嵌入结构体的方法会被提升到外部结构体,可以直接调用。

结构体比较

type Point struct {
    X, Y int
}
 
a := Point{1, 2}
b := Point{1, 2}
c := Point{3, 4}
 
fmt.Println(a == b) // true
fmt.Println(a == c) // false

所有字段都可比较的结构体可以用 == 比较。

结构体标签

type User struct {
    Name  string `json:"name"`
    Age   int    `json:"age"`
    Email string `json:"-"`
}

标签是给反射库用的元数据,比如 json 序列化时控制字段名和忽略字段。

常见写法

写法说明
type T struct{…}定义结构体类型
T{Field: v}按字段名初始化
new(T) / &T{}创建结构体指针
t.Field访问字段
t.Field = v修改字段
p.Field指针访问字段(自动解引用)
T{OtherStruct}匿名字段嵌入

特点

  • 结构体用来把相关数据打包成一个自定义类型
  • 结构体是值类型,赋值和传参会复制
  • & 取地址得到指针,指针访问字段用 . 自动解引用
  • 匿名字段实现组合,外部结构体直接访问内部的字段和方法
  • 所有字段可比较的结构体支持 == 比较
  • 结构体标签用于反射,常见于 JSON、ORM 等场景
  • 配合 Go 方法 可以给结构体绑定行为,实现面向对象

理解

结构体就是 Go 版的”对象”,把名字、年龄这些散的数据打包成一个整体。字段可以嵌套,可以嵌入,配合方法就能定义复杂的数据结构。Go 没有类,结构体就是最接近类的东西。

引出

结构体能组合数据,那想给不同类型定义统一的行为怎么办?接下来看 Go 接口,学习怎么用接口定义一组方法签名,让不同类型实现同一套行为。