struct - “数据”骨架
struct 用来自定义复杂数据结构,可以包含多个字段(属性),可以嵌套;go中的struct类型理解为类,可以定义方法,和函数定义有些许区别;
struct类型
是值类型。
1. 基本使用
type User struct {
ID int64 // 字段名和类型
Name string
CreatedAt time.Time // 可嵌入其他包的类型
}
- 零值:结构体的每个字段先取各自类型的零值,因此
var u User
时,u.Name == ""
。 - 字面量:
- 顺序式:
u := User{1, "Tom", time.Now()}
- 显式式(推荐):
u := User{ID: 1, Name: "Tom"}
- 顺序任意、可跳过字段。
- 顺序式:
2. 内存布局
Go
会按照声明顺序布置字段;为保证对齐可能产生 padding:
type A struct {
b byte // 1B
i int32 // 4B
} // 实际占 8B,后面 3 字节 padding
将相同大小的字段放一起可减少内存占用;在性能敏感的代码(如网络包结构)尤其重要。
3. 方法与接收者
func (u *User) Rename(name string) { // 指针接收者可修改原对象
u.Name = name
}
func (u User) Copy() User { // 值接收者做拷贝
return u
}
- 当结构体包含 sync.Mutex 等非复制安全字段时,应一律使用指针接收者。
- 接收者类型的一致性:值 / 指针二选一;实际可同时定义两套方法,但易混淆。
4. 组合而非继承: 匿名字段(Embedding)
type Audit struct {
CreatedAt time.Time
UpdatedAt time.Time
}
type Post struct {
Audit // 👈 匿名嵌入
ID int64
Title string
}
- 相当于 组合 + 字段提升:
p.UpdatedAt
直接可用。 - 方法同理:若
func (a *Audit) Touch()
,Post
实例也可p.Touch()
。
interface - “行为”抽象载体
1. 基本使用
type Formatter interface {
Format() string
}
type JSON struct{}
func (JSON) Format() string { return "json" }
var f Formatter = JSON{} // 不需显式声明 implements
- 隐式实现保持代码解耦;只要拥有同名同参的方法集,即满足接口。
2. 规则
接收者声明 | 方法属于谁的“方法集” | 可赋给的接口 |
---|---|---|
func (T) M() |
值 T 和 指针 *T | 既可用值又可用指针 |
func (*T) M() |
仅指针 *T | 只能用指针 |
3. 类型断言与 type switch
if s, ok := f.(string); ok { ... }
switch v := f.(type) {
case nil:
case int:
case fmt.Stringer:
}
- 失败时,单返回值断言会 panic,两返回值形式安全。
4. 底层实现
type iface struct {
tab *itab // 包含动态类型、方法指针
data unsafe.Pointer // 指向具体数据
}
- 接口赋值实际复制两份指针;频繁高并发路径(如 JSON 编解码)需注意逃逸与内存分配。
struct x interface
- 策略模式- 函数型接口
type Matcher func(s string) bool // 接口也可以是类型别名
func Filter[T any](in []T, match func(T) bool) (out []T) {
for _, v := range in { if match(v) { out = append(out, v) } }
return
}
- IoC - 面向接口编程
type Repository interface {
Save(ctx context.Context, u *User) error
Find(ctx context.Context, id int64) (*User, error)
}
type repoImpl struct { db *sql.DB }
// 实现方法
func NewRepo(db *sql.DB) Repository { return &repoImpl{db} }
- 可选配置模式(Functional Options)
type Server struct {
addr string
tls bool
}
type Option func(*Server)
func WithTLS(b bool) Option { return func(s *Server) { s.tls = b } }
func NewServer(addr string, opts ...Option) *Server {
s := &Server{addr: addr}
for _, opt := range opts { opt(s) }
return s
}
例子
插件式日志库
// ---------- 类 ----------
type Logger interface {
Log(ctx context.Context, msg string)
}
// ---------- 能 ----------
type core struct {
outs []Logger
}
func (c *core) Log(ctx context.Context, msg string) {
for _, o := range c.outs {
o.Log(ctx, msg)
}
}
// ---------- 插 ----------
type console struct{}
func (console) Log(_ context.Context, msg string) { fmt.Println(msg) }
type fileLog struct{ f *os.File }
func (l fileLog) Log(_ context.Context, msg string) {
fmt.Fprintln(l.f, msg)
}
// ---------- 使 ----------
func main() {
f, _ := os.Create("app.log")
c := &core{outs: []Logger{console{}, fileLog{f: f}}}
c.Log(context.TODO(), "hello interface + struct")
}
- struct 保存状态与资源(fileLog 持有 *os.File)。
- interface 描述行为 (Logger.Log)。
- 新插件(如 Elasticsearch logger)只需实现同样接口即可热插拔。