← 返回首页

结构体

定义结构体

package main

import "fmt"

// 定义结构体
type Person struct {
    Name    string
    Age     int
    Email   string
}

func main() {
    // 创建结构体实例
    // 方式1:按字段顺序
    p1 := Person{"张三", 25, "zhangsan@example.com"}

    // 方式2:按字段名(推荐)
    p2 := Person{
        Name:  "李四",
        Age:   30,
        Email: "lisi@example.com",
    }

    // 方式3:使用 new 关键字
    p3 := new(Person)
    p3.Name = "王五"
    p3.Age = 28
    p3.Email = "wangwu@example.com"

    fmt.Println("p1:", p1)
    fmt.Println("p2:", p2)
    fmt.Println("p3:", p3)
}

访问结构体字段

package main

import "fmt"

type Person struct {
    Name  string
    Age   int
    Email string
}

func main() {
    p := Person{Name: "张三", Age: 25}

    // 访问字段
    fmt.Println("姓名:", p.Name)
    fmt.Println("年龄:", p.Age)

    // 修改字段
    p.Age = 26
    fmt.Println("新年龄:", p.Age)

    // 使用指针
    ptr := &p
    ptr.Name = "李四"
    fmt.Println("新姓名:", p.Name)
}

结构体嵌套

package main

import "fmt"

// 地址结构体
type Address struct {
    City    string
    Street  string
    ZipCode string
}

// 用户结构体
type User struct {
    Name    string
    Age     int
    Address // 嵌套结构体
}

func main() {
    user := User{
        Name: "张三",
        Age:  25,
        Address: Address{
            City:    "北京",
            Street:  "长安街",
            ZipCode: "100000",
        },
    }

    fmt.Println("用户:", user.Name)
    fmt.Println("城市:", user.Address.City)
    fmt.Println("街道:", user.Address.Street)
}

匿名嵌套(提升字段)

package main

import "fmt"

type Point struct {
    X, Y int
}

type Circle struct {
    Point   // 匿名嵌套
    Radius int
}

func main() {
    c := Circle{
        Point:  Point{X: 10, Y: 20},
        Radius: 5,
    }

    // 可以直接访问提升的字段
    fmt.Println("X:", c.X)
    fmt.Println("Y:", c.Y)
    fmt.Println("半径:", c.Radius)

    // 也可以通过嵌套类型访问
    fmt.Println("Point.X:", c.Point.X)
}

结构体方法

package main

import "fmt"

type Rectangle struct {
    Width  float64
    Height float64
}

// 值接收者方法
func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

// 值接收者方法(不修改原结构体)
func (r Rectangle) Perimeter() float64 {
    return 2 * (r.Width + r.Height)
}

// 指针接收者方法(可以修改原结构体)
func (r *Rectangle) Scale(factor float64) {
    r.Width *= factor
    r.Height *= factor
}

func main() {
    rect := Rectangle{Width: 3, Height: 4}

    fmt.Printf("面积: %.2f\n", rect.Area())
    fmt.Printf("周长: %.2f\n", rect.Perimeter())

    rect.Scale(2)
    fmt.Printf("缩放后面积: %.2f\n", rect.Area())
}

接口

package main

import "fmt"

// 定义接口
type Shape interface {
    Area() float64
    Perimeter() float64
}

type Rectangle struct {
    Width, Height float64
}

func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

func (r Rectangle) Perimeter() float64 {
    return 2 * (r.Width + r.Height)
}

type Circle struct {
    Radius float64
}

func (c Circle) Area() float64 {
    return 3.14159 * c.Radius * c.Radius
}

func (c Circle) Perimeter() float64 {
    return 2 * 3.14159 * c.Radius
}

func printShapeInfo(s Shape) {
    fmt.Printf("面积: %.2f, 周长: %.2f\n", s.Area(), s.Perimeter())
}

func main() {
    rect := Rectangle{Width: 3, Height: 4}
    circle := Circle{Radius: 5}

    printShapeInfo(rect)
    printShapeInfo(circle)
}

空接口

package main

import "fmt"

// 空接口可以接受任何类型
func printAnything(value interface{}) {
    fmt.Println("值:", value)

    // 类型断言
    if str, ok := value.(string); ok {
        fmt.Println("是字符串,长度:", len(str))
    } else if num, ok := value.(int); ok {
        fmt.Println("是整数,值:", num)
    }
}

// 类型 switch
func checkType(value interface{}) {
    switch v := value.(type) {
    case string:
        fmt.Println("字符串:", v)
    case int:
        fmt.Println("整数:", v)
    case bool:
        fmt.Println("布尔值:", v)
    default:
        fmt.Println("未知类型")
    }
}

func main() {
    printAnything("Hello")
    printAnything(42)
    printAnything(3.14)

    checkType("Go")
    checkType(100)
    checkType(true)
}

Interface 的内部实现原理

1. Interface 的内部结构:

Go 的接口在运行时使用 eface(空接口)和 iface(非空接口)两种结构:

// 空接口 (interface{}) 的内部结构
type eface struct {
    _type *_type   // 指向类型信息的指针
    data  unsafe.Pointer  // 指向数据的指针
}

// 非空接口的内部结构
type iface struct {
    tab  *itab    // 指向方法表的指针
    data unsafe.Pointer  // 指向数据的指针
}

// 方法表结构
type itab struct {
    inter *interfacetype  // 接口类型信息
    _type *_type          // 具体类型信息
    hash  uint32           // 类型哈希值
    _     [4]byte         // 填充
    fun   [1]uintptr      // 方法指针数组(可变长)
}

2. 类型信息结构:

type _type struct {
    size       uintptr  // 类型大小
    ptrdata    uintptr  // 包含指针的字节数
    hash       uint32   // 类型哈希值
    tflag      tflag     // 类型标志
    align      uint8    // 对齐
    fieldalign uint8    // 字段对齐
    kind       uint8    // 类型种类
    alg        *typeAlg  // 算法表(hash、equal)
    gcdata     *byte    // GC 数据
    str        nameOff   // 类型名称
    ptrToThis  typeOff   // 指向此类型的指针类型
}

3. 接口赋值过程:

package main

import "fmt"

type Shape interface {
    Area() float64
}

type Rectangle struct {
    Width, Height float64
}

func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

func main() {
    rect := Rectangle{Width: 3, Height: 4}
    
    // 赋值给接口时的内部过程:
    // 1. 创建 itab 结构
    // 2. 填充接口类型信息 (inter = Shape)
    // 3. 填充具体类型信息 (_type = Rectangle)
    // 4. 填充方法指针 (fun[0] = Rectangle.Area)
    // 5. 设置 data 指向 rect
    
    var s Shape = rect
    
    // 调用方法时的内部过程:
    // 1. 通过 s.tab.fun[0] 找到 Rectangle.Area
    // 2. 将 s.data 作为接收者传递
    fmt.Println("面积:", s.Area())
}

4. 类型断言原理:

package main

import "fmt"

type Writer interface {
    Write([]byte) (int, error)
}

type File struct {
    name string
}

func (f *File) Write(data []byte) (int, error) {
    fmt.Println("写入文件:", f.name)
    return len(data), nil
}

func main() {
    var w Writer = &File{"test.txt"}
    
    // 类型断言的内部过程:
    // 1. 检查 w.tab._type 是否等于 *File
    // 2. 如果相等,返回 w.data
    // 3. 如果不相等,检查 *File 是否实现了 Writer
    
    if file, ok := w.(*File); ok {
        fmt.Println("类型断言成功:", file.name)
    }
}

5. 接口比较:

package main

import "fmt"

func main() {
    // 空接口比较
    var a, b interface{} = 1, 1
    fmt.Println("a == b:", a == b)  // true

    // 非空接口比较
    type I interface {
        M()
    }
    type T struct { x int }
    func (t T) M() {}
    
    var i1, i2 I = T{1}, T{1}
    fmt.Println("i1 == i2:", i1 == i2)  // true
    
    // 比较原理:
    // 1. 比较 tab 指针(类型是否相同)
    // 2. 比较 data 指向的值
}

6. 动态分派(Dynamic Dispatch):

package main

import "fmt"

type Writer interface {
    Write([]byte) (int, error)
}

type Buffer struct {
    data []byte
}

func (b *Buffer) Write(p []byte) (int, error) {
    b.data = append(b.data, p...)
    return len(p), nil
}

type File struct {
    name string
}

func (f *File) Write(p []byte) (int, error) {
    fmt.Printf("写入到文件 %s: %s\n", f.name, p)
    return len(p), nil
}

// 动态分派:运行时决定调用哪个方法
func writeData(w Writer, data []byte) {
    w.Write(data)
}

func main() {
    var w Writer
    
    // 动态选择实现
    w = &Buffer{}
    w.Write([]byte("hello"))
    
    w = &File{"test.txt"}
    w.Write([]byte("world"))
}

7. 接口转换(Interface Conversion):

package main

import "fmt"

type Reader interface {
    Read() string
}

type Writer interface {
    Write(string)
}

type ReadWriter interface {
    Reader
    Writer
}

type MyStruct struct {
    data string
}

func (m *MyStruct) Read() string {
    return m.data
}

func (m *MyStruct) Write(s string) {
    m.data = s
}

func main() {
    m := &MyStruct{data: "hello"}
    
    // 接口转换:从具体类型到接口
    var rw ReadWriter = m
    
    // 接口转换:从大接口到小接口
    var r Reader = rw
    fmt.Println("读取:", r.Read())
    
    // 接口转换:从小接口到大接口(需要类型断言)
    if rw2, ok := r.(ReadWriter); ok {
        rw2.Write("world")
    }
}

8. nil 接口的特殊性:

package main

import "fmt"

type Error interface {
    Error() string
}

type MyError struct {
    msg string
}

func (e *MyError) Error() string {
    return e.msg
}

func main() {
    // 1. 完全的 nil 接口
    var err1 interface{} = nil
    fmt.Println("err1 == nil:", err1 == nil)  // true
    
    // 2. 包含 nil 指针的接口(不是 nil)
    var err2 Error = (*MyError)(nil)
    fmt.Println("err2 == nil:", err2 == nil)  // false
    fmt.Println("err2 != nil 但 err2.(*MyError) == nil:", err2.(*MyError) == nil)
    
    // 3. nil 接口调用方法会 panic
    var err3 interface{} = nil
    // err3.(Error).Error() // panic
    
    // 4. 安全检查
    if e, ok := err2.(Error); ok {
        fmt.Println("err2.Error():", e.Error())
    }
}

9. 接口与指针(值接收者 vs 指针接收者):

package main

import "fmt"

type Counter interface {
    Increment()
    Get() int
}

type ValueCounter struct {
    value int
}

// 值接收者
func (v ValueCounter) Increment() {
    v.value++  // 不会修改原值
}

func (v ValueCounter) Get() int {
    return v.value
}

type PointerCounter struct {
    value int
}

// 指针接收者
func (p *PointerCounter) Increment() {
    p.value++  // 会修改原值
}

func (p *PointerCounter) Get() int {
    return p.value
}

func main() {
    // 值接收者:值和指针都可以赋值给接口
    v1 := ValueCounter{value: 0}
    var c1 Counter = v1
    c1.Increment()
    fmt.Println("v1.value:", v1.value)  // 0(未修改)
    
    v2 := &ValueCounter{value: 0}
    var c2 Counter = v2
    c2.Increment()
    fmt.Println("v2.value:", v2.value)  // 0(未修改)
    
    // 指针接收者:只有指针可以赋值给接口
    p := &PointerCounter{value: 0}
    var c3 Counter = p
    c3.Increment()
    fmt.Println("p.value:", p.value)  // 1(已修改)
    
    // var c4 Counter = PointerCounter{value: 0}  // 编译错误
}

10. 接口性能优化:

  • 避免频繁类型断言:缓存类型断言结果
  • 使用小接口:接口方法越少,性能越好
  • 避免接口嵌套:减少间接调用层级
  • 减少接口转换:避免不必要的接口类型转换
  • 使用具体类型:在性能关键路径上使用具体类型而非接口

Embed(嵌入)库使用

1. 结构体嵌入(Struct Embedding):

package main

            

            import "fmt"

            

            // 基础结构体

            type Animal struct {

                Name string

                Age  int

            }

            

            func (a Animal) Speak() {

                fmt.Printf("%s 发出声音\n", a.Name)

            }

            

            func (a Animal) Eat() {

                fmt.Printf("%s 在吃东西\n", a.Name)

            }

            

            // 嵌入 Animal

            type Dog struct {

                Animal  // 嵌入结构体

                Breed   string

            }

            

            // Dog 特有的方法

            func (d Dog) Bark() {

                fmt.Printf("%s 汪汪叫!\n", d.Name)

            }

            

            type Cat struct {

                Animal  // 嵌入结构体

                Color   string

            }

            

            func (c Cat) Meow() {

                fmt.Printf("%s 喵喵叫!\n", c.Name)

            }

            

            func main() {

                dog := Dog{

                    Animal: Animal{Name: "旺财", Age: 3},

                    Breed:  "金毛",

                }

                

                // 可以直接调用嵌入结构体的方法

                dog.Speak()   // 旺财 发出声音

                dog.Eat()     // 旺财 在吃东西

                dog.Bark()    // 旺财 汪汪叫!

                

                // 可以直接访问嵌入结构体的字段

                fmt.Printf("%s 今年 %d 岁\n", dog.Name, dog.Age)

                

                cat := Cat{

                    Animal: Animal{Name: "咪咪", Age: 2},

                    Color:  "白色",

                }

                

                cat.Speak()

                cat.Meow()

            }

2. 接口嵌入(Interface Embedding):

package main

            

                            

            

                            import "fmt"

            

                            

            

                            // 基础接口

            

                            type Reader interface {

            

                                Read([]byte) (int, error)

            

                            }

            

                            

            

                            type Writer interface {

            

                                Write([]byte) (int, error)

            

                            }

            

                            

            

                            // 嵌入接口

            

                            type ReadWriter interface {

            

                                Reader   // 嵌入 Reader 接口

            

                                Writer   // 嵌入 Writer 接口

            

                                Close() error  // 额外的方法

            

                            }

            

                            

            

                            type File struct {

            

                                name string

            

                            }

            

                            

            

                            func (f *File) Read(p []byte) (int, error) {

            

                                fmt.Println("读取文件:", f.name)

            

                                return len(p), nil

            

                            }

            

                            

            

                            func (f *File) Write(p []byte) (int, error) {

            

                                fmt.Println("写入文件:", f.name)

            

                                return len(p), nil

            

                            }

            

                            

            

                            func (f *File) Close() error {

            

                                fmt.Println("关闭文件:", f.name)

            

                                return nil

            

                            }

            

                            

            

                            func main() {

            

                                file := &File{"data.txt"}

            

                                

            

                                // File 实现了 ReadWriter 接口

            

                                var rw ReadWriter = file

            

                                

            

                                rw.Read(nil)

            

                                rw.Write(nil)

                rw.Close()

            }

3. 多重嵌入:

package main

            

            import "fmt"

            

            type Base1 struct {

                Field1 string

            }

            

            func (b Base1) Method1() {

                fmt.Println("Base1.Method1")

            }

            

            type Base2 struct {

                Field2 int

            }

            

            func (b Base2) Method2() {

                fmt.Println("Base2.Method2")

            }

            

            type Derived struct {

                Base1  // 嵌入 Base1

                Base2  // 嵌入 Base2

                Field3 bool

            }

            

            func (d Derived) Method3() {

                fmt.Println("Derived.Method3")

            }

            

            func main() {

                d := Derived{

                    Base1: Base1{Field1: "value1"},

                    Base2: Base2{Field2: 42},

                    Field3: true,

                }

                

                d.Method1()  // 来自 Base1

                d.Method2()  // 来自 Base2

                d.Method3()  // 来自 Derived

                

                fmt.Println(d.Field1, d.Field2, d.Field3)

            }

4. 嵌入与覆盖:

package main

            

            import "fmt"

            

            type Base struct {

                Name string

            }

            

            func (b Base) Greet() {

                fmt.Println("Hello from Base:", b.Name)

            }

            

            type Derived struct {

                Base

                Name string  // 覆盖 Base.Name

            }

            

            // 覆盖 Base.Greet

            func (d Derived) Greet() {

                fmt.Println("Hello from Derived:", d.Name)

                // 调用被覆盖的方法

                d.Base.Greet()

            }

            

            func main() {

                d := Derived{

                    Base: Base{Name: "BaseName"},

                    Name: "DerivedName",

                }

                

                fmt.Println("d.Name:", d.Name)        // DerivedName

                fmt.Println("d.Base.Name:", d.Base.Name)  // BaseName

                

                d.Greet()  // 调用 Derived.Greet

            }

5. 嵌入的注意事项:

  • 命名冲突:如果嵌入的多个结构体有相同的字段名,需要明确指定
  • 方法覆盖:外层结构体的方法会覆盖嵌入结构体的同名方法
  • 零值问题:嵌入的结构体字段会被初始化为零值
  • 序列化:嵌入结构体的字段会被平铺到外层结构体
package main

            

            import (

                "encoding/json"

                "fmt"

            )

            

            type Address struct {

                City    string `json:"city"`

                Country string `json:"country"`

            }

            

            type Person struct {

                Name    string `json:"name"`

                Address // 嵌入 Address

            }

            

            func main() {

                p := Person{

                    Name: "张三",

                    Address: Address{

                        City:    "北京",

                        Country: "中国",

                    },

                }

                

                // JSON 序列化时,Address 的字段会被平铺

                data, _ := json.Marshal(p)

                fmt.Println(string(data))

                // 输出: {"name":"张三","city":"北京","country":"中国"}

            }

6. 嵌入与组合(Composition):

package main

            

            import "fmt"

            

            // 基础组件

            type Engine struct {

                Power int

            }

            

            func (e *Engine) Start() {

                fmt.Printf("引擎启动,功率: %d\n", e.Power)

            }

            

            type Wheels struct {

                Count int

            }

            

            func (w *Wheels) Rotate() {

                fmt.Printf("%d 个轮子转动\n", w.Count)

            }

            

            // 通过嵌入组合成汽车

            type Car struct {

                Engine

                Wheels

                Brand string

            }

            

            func (c Car) Drive() {

                c.Start()

                c.Rotate()

                fmt.Printf("%s 汽车行驶中\n", c.Brand)

            }

            

            func main() {

                car := Car{

                    Engine: Engine{Power: 200},

                    Wheels: Wheels{Count: 4},

                    Brand:  "特斯拉",

                }

                car.Drive()

            }

7. 嵌入与接口(Interface Embedding):

package main

            

            import "fmt"

            

            // 基础接口

            type Reader interface {

                Read([]byte) (int, error)

            }

            

            type Writer interface {

                Write([]byte) (int, error)

            }

            

            type Closer interface {

                Close() error

            }

            

            // 组合多个接口

            type ReadWriteCloser interface {

                Reader

                Writer

                Closer

            }

            

            // 嵌入接口实现

            type MyFile struct {

                name string

                data []byte

            }

            

            func (f *MyFile) Read(p []byte) (int, error) {

                fmt.Println("读取文件:", f.name)

                return len(p), nil

            }

            

            func (f *MyFile) Write(p []byte) (int, error) {

                f.data = append(f.data, p...)

                fmt.Println("写入文件:", f.name)

                return len(p), nil

            }

            

            func (f *MyFile) Close() error {

                fmt.Println("关闭文件:", f.name)

                return nil

            }

            

            func main() {

                file := &MyFile{name: "test.txt"}

                

                // MyFile 自动实现了 ReadWriteCloser 接口

                var rwc ReadWriteCloser = file

                

                rwc.Write([]byte("hello"))

                rwc.Read(nil)

                rwc.Close()

            }

8. 嵌入与并发(Concurrency):

package main

            

            import (

                "fmt"

                "sync"

                "time"

            )

            

            // 基础缓存结构

            type Cache struct {

                data map[string]string

            }

            

            func (c *Cache) Get(key string) (string, bool) {

                val, ok := c.data[key]

                return val, ok

            }

            

            func (c *Cache) Set(key, value string) {

                c.data[key] = value

            }

            

            // 嵌入并添加并发安全

            type SafeCache struct {

                Cache

                mu sync.RWMutex

            }

            

            func (sc *SafeCache) Get(key string) (string, bool) {

                sc.mu.RLock()

                defer sc.mu.RUnlock()

                return sc.Cache.Get(key)

            }

            

            func (sc *SafeCache) Set(key, value string) {

                sc.mu.Lock()

                defer sc.mu.Unlock()

                sc.Cache.Set(key, value)

            }

            

            func main() {

                cache := &SafeCache{

                    Cache: Cache{data: make(map[string]string)},

                }

                

                // 并发测试

                var wg sync.WaitGroup

                

                for i := 0; i < 10; i++ {

                    wg.Add(1)

                    go func(n int) {

                        defer wg.Done()

                        key := fmt.Sprintf("key%d", n)

                        value := fmt.Sprintf("value%d", n)

                        cache.Set(key, value)

                        fmt.Printf("设置: %s = %s\n", key, value)

                    }(i)

                }

                

                wg.Wait()

                

                // 读取数据

                for i := 0; i < 10; i++ {

                    key := fmt.Sprintf("key%d", i)

                    if val, ok := cache.Get(key); ok {

                        fmt.Printf("读取: %s = %s\n", key, val)

                    }

                }

            }

9. 嵌入与测试(Testing):

package main

            

            import "fmt"

            

            // 数据库接口

            type Database interface {

                Query(sql string) ([]string, error)

                Execute(sql string) error

            }

            

            // 真实数据库实现

            type RealDB struct {

                connectionString string

            }

            

            func (db *RealDB) Query(sql string) ([]string, error) {

                fmt.Printf("执行查询: %s\n", sql)

                return []string{"result1", "result2"}, nil

            }

            

            func (db *RealDB) Execute(sql string) error {

                fmt.Printf("执行SQL: %s\n", sql)

                return nil

            }

            

            // 测试用的模拟数据库

            type MockDB struct {

                queries []string

                results [][]string

            }

            

            func (m *MockDB) Query(sql string) ([]string, error) {

                m.queries = append(m.queries, sql)

                return m.results[len(m.results)-1], nil

            }

            

            func (m *MockDB) Execute(sql string) error {

                m.queries = append(m.queries, sql)

                return nil

            }

            

            func (m *MockDB) GetQueries() []string {

                return m.queries

            }

            

            // 使用嵌入的服务

            type UserService struct {

                Database

            }

            

            func (s *UserService) GetUsers() ([]string, error) {

                return s.Query("SELECT * FROM users")

            }

            

            func (s *UserService) CreateUser(name string) error {

                sql := fmt.Sprintf("INSERT INTO users (name) VALUES ('%s')", name)

                return s.Execute(sql)

            }

            

            func main() {

                // 使用真实数据库

                realDB := &RealDB{connectionString: "localhost:3306"}

                realService := &UserService{Database: realDB}

                

                realService.CreateUser("张三")

                users, _ := realService.GetUsers()

                fmt.Println("用户列表:", users)

                

                // 使用模拟数据库(测试场景)

                mockDB := &MockDB{

                    results: [][]string{{"user1", "user2"}},

                }

                mockService := &UserService{Database: mockDB}

                

                mockService.CreateUser("李四")

                mockUsers, _ := mockService.GetUsers()

                fmt.Println("模拟用户列表:", mockUsers)

                fmt.Println("执行的查询:", mockDB.GetQueries())

            }

10. 嵌入与中间件(Middleware):

package main

            

            import "fmt"

            

            // 基础处理器

            type Handler interface {

                Handle(string) string

            }

            

            type BaseHandler struct{}

            

            func (h BaseHandler) Handle(request string) string {

                return "处理: " + request

            }

            

            // 日志中间件

            type LoggingMiddleware struct {

                Handler

            }

            

            func (l *LoggingMiddleware) Handle(request string) string {

                fmt.Printf("日志: 收到请求 - %s\n", request)

                result := l.Handler.Handle(request)

                fmt.Printf("日志: 返回结果 - %s\n", result)

                return result

            }

            

            // 认证中间件

            type AuthMiddleware struct {

                Handler

                token string

            }

            

            func (a *AuthMiddleware) Handle(request string) string {

                fmt.Printf("认证: 验证令牌\n")

                return a.Handler.Handle(request)

            }

            

            func main() {

                // 组合中间件

                base := BaseHandler{}

                withLogging := &LoggingMiddleware{Handler: base}

                withAuth := &AuthMiddleware{Handler: withLogging, token: "secret"}

                

                result := withAuth.Handle("GET /users")

                fmt.Println("最终结果:", result)

            }

11. 嵌入的最佳实践:

  • 优先使用组合而非继承:Go 不支持继承,嵌入是实现代码复用的最佳方式
  • 明确嵌入的意图:嵌入应该是"is-a"关系,而不是"has-a"关系
  • 避免过度嵌入:过多的嵌套会使代码难以理解和维护
  • 文档化嵌入行为:明确说明嵌入的字段和方法的使用方式
  • 考虑接口嵌入:接口嵌入可以创建更灵活的组合方式

标准库 embed 使用

1. 基本使用:

Go 1.16 引入了 embed 包,允许在编译时将静态文件嵌入到二进制文件中。

package main

            

                            

            

                            import (

            

                                "embed"

            

                                "fmt"

            

                            )

            

                            

            

                            //go:embed hello.txt

            

                            var content string

            

                            

            

                            func main() {

            

                                fmt.Println(content)

            

                            }

2. 嵌入单个文件:

package main

            

                            

            

                            import (

            

                                _ "embed"

            

                                "fmt"

            

                            )

            

                            

            

                            //go:embed config.yaml

            

                            var config string

            

                            

            

                            //go:embed version.txt

            

                            var version string

            

                            

            

                            func main() {

            

                                fmt.Println("配置文件内容:")

            

                                fmt.Println(config)

            

                                fmt.Println("版本信息:")

            

                                fmt.Println(version)

            

                            }

3. 嵌入多个文件:

package main

            

                            

            

                            import (

            

                                "embed"

            

                                "fmt"

            

                                "io/fs"

            

                            )

            

                            

            

                            //go:embed templates/*.html

            

                            var templates embed.FS

            

                            

            

                            func main() {

            

                                // 读取嵌入的文件

            

                                content, err := templates.ReadFile("templates/index.html")

            

                                if err != nil {

            

                                    fmt.Println("错误:", err)

            

                                    return

            

                                }

            

                                fmt.Println(string(content))

            

                                

            

                                // 遍历嵌入的文件

            

                                fs.WalkDir(templates, ".", func(path string, d fs.DirEntry, err error) error {

            

                                    if err != nil {

            

                                        return err

            

                                    }

            

                                    fmt.Printf("文件: %s\n", path)

            

                                    return nil

            

                                })

            

                            }

4. 嵌入整个目录:

package main

            

                            

            

                            import (

            

                                "embed"

            

                                "fmt"

            

                                "io/fs"

            

                                "net/http"

            

                            )

            

                            

            

                            //go:embed static/*

            

                            var staticFiles embed.FS

            

                            

            

                            func main() {

            

                                // 创建文件服务器

            

                                staticFS, err := fs.Sub(staticFiles, "static")

            

                                if err != nil {

            

                                    fmt.Println("错误:", err)

            

                                    return

            

                                }

            

                                

            

                                http.Handle("/static/", http.FileServer(http.FS(staticFS)))

            

                                

            

                                fmt.Println("服务器启动在 http://localhost:8080")

            

                                http.ListenAndServe(":8080", nil)

            

                            }

5. 嵌入多个模式:

package main

            

                            

            

                            import (

            

                                "embed"

            

                                "fmt"

            

                            )

            

                            

            

                            //go:embed config/*.yaml

            

                            //go:embed templates/*.html

            

                            //go:embed static/*

            

                            var files embed.FS

            

                            

            

                            func main() {

            

                                // 读取不同类型的文件

            

                                config, _ := files.ReadFile("config/app.yaml")

            

                                template, _ := files.ReadFile("templates/index.html")

            

                                

            

                                fmt.Println("配置:", string(config))

            

                                fmt.Println("模板:", string(template))

            

                            }

6. 嵌入为字节数组:

package main

            

                            

            

                            import (

            

                                "embed"

            

                                "fmt"

            

                            )

            

                            

            

                            //go:embed image.png

            

                            var imageData []byte

            

                            

            

                            func main() {

            

                                fmt.Printf("图片大小: %d 字节\n", len(imageData))

            

                            }

7. Web 服务器示例:

package main

            

                            

            

                            import (

            

                                "embed"

            

                                "fmt"

            

                                "io/fs"

            

                                "net/http"

            

                            )

            

                            

            

                            //go:embed templates/*

            

                            var templates embed.FS

            

                            

            

                            //go:embed static/*

            

                            var static embed.FS

            

                            

            

                            func homeHandler(w http.ResponseWriter, r *http.Request) {

            

                                content, err := templates.ReadFile("templates/index.html")

            

                                if err != nil {

            

                                    http.Error(w, "文件未找到", http.StatusNotFound)

            

                                    return

            

                                }

            

                                w.Header().Set("Content-Type", "text/html")

            

                                w.Write(content)

            

                            }

            

                            

            

                            func main() {

            

                                // 设置静态文件服务

            

                                staticFS, _ := fs.Sub(static, "static")

            

                                http.Handle("/static/", http.FileServer(http.FS(staticFS)))

            

                                

            

                                // 设置首页

            

                                http.HandleFunc("/", homeHandler)

            

                                

            

                                fmt.Println("服务器启动在 http://localhost:8080")

            

                                http.ListenAndServe(":8080", nil)

            

                            }

8. 嵌入的注意事项:

  • 指令位置//go:embed 指令必须紧邻变量声明,中间不能有空行
  • 变量类型:可以是 string[]byteembed.FS
  • 路径限制:只能嵌入当前包所在目录及其子目录的文件
  • 文件大小:嵌入大文件会增加二进制文件的大小
  • 开发模式:在开发时,文件路径是相对于源文件的

9. embed 最佳实践:

  • 用于静态资源:HTML 模板、CSS、JavaScript、图片等
  • 配置文件:默认配置、示例配置等
  • 单文件应用:创建无需外部依赖的可执行文件
  • 版本控制:将嵌入的文件纳入版本控制
  • 测试数据:将测试数据嵌入到测试代码中

结构体标签

package main

import (
    "encoding/json"
    "fmt"
    "reflect"
)

type User struct {
    ID       int    `json:"id"`
    Name     string `json:"name"`
    Email    string `json:"email"`
    Password string `json:"-"` // 不进行 JSON 序列化
}

func main() {
    user := User{
        ID:       1,
        Name:     "张三",
        Email:    "zhangsan@example.com",
        Password: "secret",
    }

    // JSON 序列化
    jsonData, _ := json.Marshal(user)
    fmt.Println("JSON:", string(jsonData))

    // 反射获取标签
    t := reflect.TypeOf(user)
    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i)
        fmt.Printf("字段: %s, 标签: %s\n", field.Name, field.Tag.Get("json"))
    }
}