个人 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 开始搜索的例子, 无论是速度还是效果, 都和自己维护到知识库比起来要胜出不少, 甚至数量级的差异

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 225,208评论 6 524
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 96,502评论 3 405
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 172,496评论 0 370
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 61,176评论 1 302
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 70,185评论 6 401
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 53,630评论 1 316
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 41,992评论 3 431
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 40,973评论 0 280
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 47,510评论 1 325
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 39,546评论 3 347
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 41,659评论 1 355
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 37,250评论 5 351
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,990评论 3 340
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 33,421评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 34,569评论 1 277
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 50,238评论 3 382
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 46,732评论 2 366

推荐阅读更多精彩内容