讲解

结构体(struct)把多个不同类型的字段组合成一个新类型,是 Go 里建模现实事物的主力:一个学生有姓名、年龄、成绩,一个订单有编号、金额、状态,都用结构体表达。type 名字 struct { 字段 类型 } 定义类型,字段名首字母大写表示「导出」(包外可见),小写则包内私有——这条大小写规则贯穿整个 Go。

创建结构体实例推荐「字段名: 值」的字面量写法,清晰且不受字段顺序影响:Student{Name: "小明", Age: 20}。按字段顺序的写法 Student{"小明", 20} 虽然合法,但结构体加字段时容易错位,生产代码应避免。不初始化则得到零值结构体,每个字段都是各自类型的零值。

结构体可以嵌套:Person 里放一个 Address 类型的字段,访问就是 p.Address.City 逐级点下去。Go 没有类继承,组合是唯一的复用方式。另外,全部由可比较字段组成的结构体本身也可以用 == 比较,两个实例所有字段相等则整体相等,这在测试里断言结果时特别好用。

示例

定义、初始化与字段访问:

package main

import "fmt"

type Student struct {
	Name  string
	Age   int
	Score float64
}

func main() {
	s1 := Student{Name: "小明", Age: 20, Score: 92.5} // 推荐:带字段名
	s2 := Student{"小红", 22, 88.0}                    // 按顺序,不推荐
	var s3 Student                                     // 零值结构体
	s3.Name = "小刚"

	fmt.Println(s1.Name, "的成绩:", s1.Score)
	fmt.Println(s2, s3)
}

嵌套结构体与指针访问:

package main

import "fmt"

type Address struct {
	City    string
	ZipCode string
}

type Person struct {
	Name    string
	Address Address
}

func main() {
	p := Person{
		Name:    "小明",
		Address: Address{City: "北京", ZipCode: "100000"},
	}
	fmt.Println(p.Name, "住在", p.Address.City)

	pp := &p
	pp.Address.City = "上海" // 指针访问字段自动解引用,不用写 (*pp)
	fmt.Println("修改后:", p.Address.City)
}

结构体的相等比较:

package main

import "fmt"

type Point struct {
	X, Y int
}

func main() {
	a := Point{1, 2}
	b := Point{1, 2}
	c := Point{2, 1}
	fmt.Println(a == b) // true:所有字段都相等
	fmt.Println(a == c) // false
}

常见坑

  • 按字段顺序初始化:Student{"小明", 20} 在结构体调整字段后静默错位或编译失败。一律写字段名,go vet 也会对无名复合字面量报警。
  • 期待结构体里有 nil 字段零值:值类型的零值是零值本身——int 字段是 0 而不是 nil,嵌套结构体字段是零值结构体。需要表达「未设置」才用指针字段。
  • 含切片的结构体用 == 比较:字段里有切片、map、函数时整个结构体不可比较,编译报错。需要深度比较用 reflect.DeepEqual(测试里)或自行逐字段比较。
  • 大结构体按值传来传去:结构体传参是拷贝,字段很多或含大数组时每次调用都复制,性能浪费。大结构体传指针,这也是下一章方法接收者的选择依据。

小结

结构体组合字段建模,字段名字面量初始化,嵌套表达组合关系,纯可比较字段的结构体支持 ==。下一节学习函数:多返回值、可变参数与闭包。