个人 Go 知识库代码片段整理

写在前面

AI时代的技术发展, 确实对个人知识库起到了非常大的帮助, 列举2个非常重要的点

  • LLM能够快速学习 -> 个人学习受到输入/理解/记忆等方面的限制, 通常需要漫长的学习过程, LLM 却能做到快+上限不断提高
  • 基于 LLM 的知识库, 比个人自己不断搜集整理的知识库, 简单+轻松, 比如我自己的知识库, 建立索引+维护索引的难度不断提高, 维护的成本越来越大, 而 LLM 却相反, 反而是数据越多越智能

Go代码片段

  • hello

快速跑 go 代码: main.go -> test

func main() {
    fmt.Println(max(1, 2))
}

AI 提供的内容上下文更清晰, 自己知识库的内容反而需要再次查找下才能真正use it

func main() {
    // go tool trace t.out
    f, _ := os.Create("t.out")
    _ = trace.Start(f)
    defer trace.Stop()

    ch := make(chan string)
    go func() {
        ch <- "this is a test"
    }()

    <-ch
}
  • 保留了一些 gin 使用 demo, 最后只留下了关于 req 定义的内容, 这个属于内部 API 规范逐渐形成的
  • 关于 req 定义, 一个常见的错误: tag 漏写/写错
package types

type Req struct {
    // GET 分页查询
    Page     int `form:"page,default=1" binding:"gte=1"`
    PageSize int `form:"page_size,default=10" binding:"gte=1,lte=100"`

    // 导出
    IsExport bool `form:"is_export"`

    IsMock bool `form:"is_mock"` // mock数据, 只测试阶段使用
}

type XxxListRow struct{}

// XxxListResp 分页查询
type XxxListResp struct {
    Rows []*XxxListRow `json:"rows"`
    Cnt  int64         `json:"cnt"`
}

type BaseResp struct {
    Code int64  `json:"code"` // 0: success, 1: error
    Msg  string `json:"msg"`  // error message, 0-ok, 1-error
    Data any    `json:"data"` // response data
}

// https://www.cloudwego.io/docs/hertz/tutorials/basic-feature/binding-and-validate/
var req struct {
    User     string `form:"user" json:"user" query:"user" vd:"(len($) > 0 && len($) < 128); msg:'Illegal format'"`
    Password string `form:"password" json:"password" query:"password" vd:"(len($) > 0 && len($) < 128); msg:'Illegal format'"`
}

搜索结果和自己知识库的记录完全匹配 Go语言编程技巧

// 摘录部分
// Tip #51 避免使用init()
func init() {
    fmt.Println("package basic init") // Tip #6 下划线导入
}

// Status Tip #53:枚举从1开始用于分类,从0用于默认情况
type Status int

const (
    StatusNone Status = iota
    StatusOk
)

// TrackTime Tip #1 一行代码测量函数的执行时间
func TrackTime(start time.Time) time.Duration {
    elapsed := time.Since(start)
    fmt.Println("elapsed: ", elapsed)
    return elapsed
}

// MultistageDefer Tip #2 多阶段 defer -> 在另一个函数的开头和结尾处执行一个函数
func MultistageDefer() func() {
    fmt.Println("run init")
    return func() {
        fmt.Println("run cleanup")
    }
}
  • 自己刷题积累了一些代码片段和模板, 使用 AI 获取的优质内容完全可以吊打自己写的这些
  • 一点碎碎念: 大厂刷题完全是浪费时间, 刷题确实可以让你保持 coding 的手感, 但是成就感完全没法和拿得出手的开源项目比
// arr.go
package template

import (
    "container/ring"
    "fmt"
)

func arr() {
    // arr array: fixed-length; Using pointer
    a1 := [...]int{1, 2, 3}
    a2 := [3]int{1, 2, 3}
    fmt.Println(a1, a2)

    // fmt.Printf("%x %x %t %T\n", arr1, arr2, arr1 == arr2, arr1) // %x array/slice %t bool %T type

    // a slice: pointer(数据会发生共享!)+len()+cap(); not comparable; nil
    // make([]int, 0, 3) // make([]int, 3) 会导致前 3 个已赋值, append() 从第 4 个开始
    // https://github.com/golang/go/wiki/SliceTricks
    // https://ueokande.github.io/go-slice-tricks/
    a := []int{0, 1, 2} // a := make([]int, 3) 预先分配, 使用 a[i]
    if len(a) == 0 {    // nil时len==0
        fmt.Println("empty")
    }
    a[2] = 4
    // var a []int // a=nil; a = []int{} // a!=nil
    // make([]T, len, cap) = make([]T, cap)[:len]
    // append() cap扩容
    a = append(a[:10], a[10+1:]...) // 删除第i个值
    a = append([]int(nil), a...)    // copy
    var x int
    a = append(a, x)                 // push
    x, a = a[len(a)-1], a[:len(a)-1] // stack
    x, a = a[0], a[1:]               // queue
    // tail = (tail+1)%N // 环形队列.固定长度缓冲区读写
    ring.New(5)

    // 矩阵
    r, c := 3, 4
    mat := make([][]int, r)
    for i := 0; i < r; i++ {
        mat[i] = make([]int, c)
    }

    var cnt1, cnt2 [26]int
    fmt.Println(cnt1 == cnt2) // true
}

// arrFlip 数组翻转
func arrFlip[T any](a []T) {
    // n := len(a)
    
    // for i := 0; i < n/2; i++ {
    //  a[i], a[n-1-i] = a[n-1-i], a[i]
    // }
    l, r := 0, len(a)-1
    for l < r {
        a[l], a[r] = a[r], a[l]
        l, r = l+1, r-1
    }
}

func itoa(n int) (b []byte) {
    for ; n > 0; n /= 10 {
        b = append(b, byte(n%10+'0'))
    }
    arrFlip(b)
    return b
}

我在面试中经常会问的一个问题: 使用过哪些 go 并发编程原语 or 模式, 因为这是 go 编程中最容易犯错的内容

// or-done pattern by recursive
func or(channels ...<-chan any) <-chan any {
    // 特殊情况,只有零个或者1个chan
    switch len(channels) {
    case 0:
        return nil
    case 1:
        return channels[0]
    }

    orDone := make(chan any)
    go func() {
        defer close(orDone)

        switch len(channels) {
        case 2: // 2个也是一种特殊情况
            select {
            case <-channels[0]:
            case <-channels[1]:
            }
        default: // 超过两个,二分法递归处理
            m := len(channels) / 2
            select {
            case <-or(channels[:m]...):
            case <-or(channels[m:]...):
            }
        }
    }()

    return orDone
}
// sync 包
// sync包的同步原语在使用后是不能复制的: vet.noCopy
var mu sync.Mutex // 零值不需要额外初始化

type Counter struct { // 传参是必须使用指针, 否则会导致mu复制
    mu  sync.Mutex // 1.放在要控制的字段上面并空行 2.内嵌字段 3.不可重入
    cnt uint64
}

// 1.封装成方法 2.读写都需要

func (c *Counter) Incr() {
    c.mu.Lock()
    c.cnt++
    c.mu.Unlock() // ‼️ 可以被任意协程调用 -> 谁申请谁释放
}

func (c *Counter) Cnt() uint64 {
    c.mu.Lock()
    defer c.mu.Unlock() // ✅ 推荐

    return c.cnt
}

func (c *Counter) Cnt2() uint64 {
    v := atomic.LoadInt32((*int32)(unsafe.Pointer(&c.mu))) // 使用 unsafe 获取未暴露的字段: mutex.state
    return uint64(v)
}

非 OOP 其实对设计模式一词相当无感, 其实设计模式更多是一些约定俗成习以为常, 比如 conf 中使用 WithOptionXxx, 任何读过 go 项目源码的都不陌生

// option.go
package dp

var defaultStuffClient = stuffClient{
    retries: 3,
    timeout: 2,
}

type StuffClientOption func(*stuffClient)

func WithRetries(r int) StuffClientOption {
    return func(o *stuffClient) {
        o.retries = r
    }
}

func WithTimeout(t int) StuffClientOption {
    return func(o *stuffClient) {
        o.timeout = t
    }
}

type StuffClient interface {
    DoStuff() error
}
type stuffClient struct {
    conn    Connection
    timeout int
    retries int
}
type Connection struct{}

func NewStuffClient(conn Connection, opts ...StuffClientOption) StuffClient {
    client := defaultStuffClient
    for _, o := range opts {
        o(&client)
    }

    client.conn = conn
    return client
}

func (c stuffClient) DoStuff() error {
    return nil
}

写在最后

写到 blog 中内容, 不到知识库中清理掉的 go 代码片段的 1/1000, 基本上平时不回顾知识库, 想要找到需要的代码片段的成本, 和从 0 开始搜索成本都差不多, 而且更麻烦的点在于, 知识库积累得越多, 维护索引成本越高, 使用成本也越高, 可以说是一件没法持续的事, 幸好 AI 提供了一条新路径, 上面也列举了很多从 0 开始搜索的例子, 无论是速度还是效果, 都和自己维护到知识库比起来要胜出不少, 甚至数量级的差异

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容