讲解
结构体(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(测试里)或自行逐字段比较。
- 大结构体按值传来传去:结构体传参是拷贝,字段很多或含大数组时每次调用都复制,性能浪费。大结构体传指针,这也是下一章方法接收者的选择依据。
小结
结构体组合字段建模,字段名字面量初始化,嵌套表达组合关系,纯可比较字段的结构体支持 ==。下一节学习函数:多返回值、可变参数与闭包。