Quiet
  • 主页
  • 归档
  • 分类
  • 标签
  • 链接
  • 关于我

bajiu

  • 主页
  • 归档
  • 分类
  • 标签
  • 链接
  • 关于我
Quiet主题
  • golang

golang 中的 struct 和 interface

bajiu
服务端

2025-06-24 19:58:00

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

  1. 策略模式- 函数型接口
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
}
  1. 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} }
  1. 可选配置模式(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)只需实现同样接口即可热插拔。
上一篇

golang 实现继承

下一篇

go mod 基础

©2025 By bajiu.