Go 语言结构体¶
结构体把若干命名字段组合成一个值,是 Go 建模业务数据的主要工具。Go 没有类;行为通过给 命名类型定义方法来表达,复用通常依靠组合和接口。
定义与初始化¶
package main
import "fmt"
type User struct {
ID int64
Name string
Active bool
}
func main() {
var zero User
a := User{ID: 1, Name: "Alice", Active: true}
b := &User{ID: 2, Name: "Bob"}
fmt.Println(zero, a.Name, b.ID)
}
字段会得到各自类型的零值。推荐使用带字段名的复合字面量,它能抵抗字段顺序变化;
不带字段名的 User{1, "Alice", true} 更脆弱,且跨包使用时可读性较差。
字段可见性与方法¶
字段名首字母大写表示从其他包可导出,小写字段仅包内可见。可见性由标识符决定,不是由
public 或 private 关键字决定。
type Rectangle struct {
Width float64
Height float64
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
func (r *Rectangle) Scale(factor float64) {
r.Width *= factor
r.Height *= factor
}
第一个差异示例:Area 只读取值,适合值接收者;Scale 修改状态,必须使用指针接收者。
同一类型的方法接收者风格应保持一致,尤其不要复制包含 sync.Mutex 的结构体。
相等性与复制¶
若所有字段都可比较,结构体可直接使用 ==。包含切片、map 或函数字段的结构体不可整体比较。
type Point struct{ X, Y int }
p1 := Point{X: 1, Y: 2}
p2 := p1
p2.X = 9
fmt.Println(p1 == p2) // false
第二个差异示例:普通字段随结构体复制而独立;若字段是切片,复制结构体只复制切片描述符, 两个结构体仍可能共享底层数组。需要深拷贝时必须明确复制引用式字段。
标签与编码¶
结构体标签是供反射库读取的字符串元数据:
标签本身不会执行验证或转换。encoding/json 只能直接处理导出字段,未导出字段即使有标签也不会编码。
常见错误¶
- 跨包依赖未导出字段,代码无法编译。
- 用位置字面量初始化字段较多的结构体,字段调整后语义错位。
- 复制含互斥锁的结构体,产生两个失去同步关系的锁副本。
- 认为结构体复制一定是深拷贝,忽略切片、map、指针字段共享数据。
- 把所有字段都导出,使类型不变量无法由构造函数和方法维护。
性能与工程实践¶
字段顺序可能因内存对齐影响结构体尺寸,可用 unsafe.Sizeof 观察,但不要为了少量字节牺牲领域可读性。
高频创建的大量小对象才值得评估布局。API 中优先让结构体表达清晰的数据契约;当零值有用时,
尽量让类型无需构造函数即可工作。需要并发共享时,应明确哪些字段受锁保护,而不是暴露内部可变字段。