首页 / Go 语言入门教程 / 结构体定义

Go 语言入门教程

结构体定义

本教程共 80 篇 · 第 37 篇 · 更新于 2026-07-27 · 约 7 分钟阅读

GoGo 入门教程结构体Struct字段标签匿名字段

37. 结构体定义

本节目标:学会用 struct 定义自己的数据类型,理解字段、标签、匿名字段的概念。

什么是结构体

结构体(Struct)就是把多个不同类型的变量打包在一起,形成一个新的类型。比如一个「人」有名字、年龄、身高,你就可以用结构体把它们组织起来。

type Person struct {
    Name string
    Age  int
}

这样你就定义了一个 Person 类型,它有两个字段:Name 是字符串,Age 是整数。

定义和使用

基本用法

package main

import "fmt"

type Person struct {
    Name string
    Age  int
}

func main() {
    // 创建结构体
    p := Person{
        Name: "Alice",
        Age:  30,
    }

    fmt.Println(p)        // {Alice 30}
    fmt.Println(p.Name)   // Alice  用 . 访问字段
    fmt.Println(p.Age)    // 30

    // 修改字段
    p.Age = 31
    fmt.Println(p.Age)    // 31
}

. 来访问和修改字段,和大多数语言一样。

省略字段名

如果你不写字段名,按顺序赋值也行:

p := Person{"Alice", 30}
Warning

不推荐这种写法。一旦结构体加了新字段,顺序就全乱了。养成用字段名的习惯,代码更安全。

结构体是值类型

和数组一样,结构体是值类型。赋值时会拷贝一份完整的数据:

p1 := Person{"Alice", 30}
p2 := p1

p2.Name = "Bob"
fmt.Println(p1.Name) // Alice  p1 没变
fmt.Println(p2.Name) // Bob    只有 p2 变了

如果你想让两个变量共享同一份数据,用指针:

p1 := &Person{"Alice", 30}
p2 := p1

p2.Name = "Bob"
fmt.Println(p1.Name) // Bob  p1 也变了,因为指向同一个

字段标签

结构体字段可以加标签(tag),这是一段附加的元信息,用反引号包裹:

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

标签最常见的用途是 JSON 序列化。上面的标签告诉 encoding/json 包:序列化时把 Name 输出为 "name"Email 为空时省略。

package main

import (
    "encoding/json"
    "fmt"
)

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

func main() {
    u := User{Name: "Alice", Age: 30}
    data, _ := json.Marshal(u)
    fmt.Println(string(data))
    // {"name":"Alice","age":30}  email为空被省略了

    u.Email = "alice@example.com"
    data, _ = json.Marshal(u)
    fmt.Println(string(data))
    // {"name":"Alice","age":30,"email":"alice@example.com"}
}
Note

标签的格式是 key:"value",可以写多组,用空格分隔,比如 `json:"name" xml:"name" bson:"name"`

常见的标签类型:

标签前缀用途
jsonJSON 序列化/反序列化
xmlXML 编解码
db数据库映射
yamlYAML 配置文件
validate数据校验

匿名字段(嵌入类型)

结构体字段可以只有类型名,不写字段名,这叫匿名字段:

type Animal struct {
    Name string
}

type Dog struct {
    Animal // 匿名字段,嵌入了 Animal 类型
    Breed  string
}

嵌入 Animal 后,Dog 可以直接访问 Animal 的字段,这叫「字段提升」:

package main

import "fmt"

type Animal struct {
    Name string
}

type Dog struct {
    Animal
    Breed string
}

func main() {
    d := Dog{
        Animal: Animal{Name: "Rex"},
        Breed:  "Labrador",
    }

    // 直接访问提升的字段
    fmt.Println(d.Name)     // Rex  等价于 d.Animal.Name
    fmt.Println(d.Breed)    // Labrador

    // 也可以通过完整路径访问
    fmt.Println(d.Animal.Name) // Rex
}

匿名字段实现了 Go 的「组合优于继承」理念。后面的章节会详细讲嵌入和方法提升。

Tip

匿名字段不限于结构体,也可以嵌入基础类型。比如 type MyType struct { int },这样 MyType 就有一个匿名的 int 字段。

结构体比较

如果结构体的所有字段都是可比较的,结构体本身也可以用 == 比较:

type Point struct {
    X, Y int
}

p1 := Point{1, 2}
p2 := Point{1, 2}
p3 := Point{1, 3}

fmt.Println(p1 == p2) // true
fmt.Println(p1 == p3) // false

但如果字段里有切片、map 等不可比较的类型,就不能用 == 了,编译会报错。

小结

结构体是 Go 组织数据的核心方式:

  • type Name struct { ... } 定义
  • 字段标签给序列化框架提供元信息
  • 匿名字段实现类型嵌入和字段提升
  • 结构体是值类型,赋值会拷贝

下一章讲结构体的初始化方式和匿名嵌入的深入用法。