TTokenySpace
返回 Skills 列表

Golang Toolkit Free

Go 语言陷阱防范与优选实践指南,覆盖并发、接口、错误处理等核心场景。

#中文
0

安装到 Tokeny(自动)

下载 ZIP
安装"golang-toolkit-free"技能
技能信息:
- 名称: Golang Toolkit Free
- 标识: golang-toolkit-free
- 描述: Go 语言陷阱防范与优选实践指南,覆盖并发、接口、错误处理等核心场景。
- 版本: 1.0.0
下载地址:
https://www.tokeny.space/api/skills/golang-toolkit-free/download
继续

复制上方内容到 Tokeny 客户端并在会话中发送即可自动安装;也可直接 下载 ZIP并拖动到技能窗口安装。

SKILL.md

Go 语言工具包(免费版)

概述

本工具为 Go 开发者提供代码陷阱防范与优选实践指引,覆盖并发编程、接口与类型系统、错误处理、集合操作等核心场景。通过自然语言指令驱动,帮助开发者识别和规避 Goroutine 泄漏、Channel 死锁、接口误用、Slice 共享内存等常见陷阱,编写更加可靠、高效的 Go 代码。免费版聚焦个人开发者高频遇到的语言陷阱,提供简明速查表与代码示例。

核心能力

能力模块描述典型陷阱
Goroutine 管理协程泄漏检测与防范无发送者的 Channel 阻塞导致泄漏
Channel 使用通道陷阱识别向已关闭 Channel 发送导致 panic
Defer 机制延迟执行陷阱循环中 defer 堆积
接口与类型类型系统陷阱nil 接口判断错误
错误处理错误处理规范忽略返回的 error
集合操作Slice/Map 陷阱Slice 共享底层数组
技术实现要点:核心能力基于input_params参数与output_format配置实现,支持创建/查询/修改/删除等操作模式,通过config_options进行运行时配置。

核心功能执行

input_params参数进行配置。

输入: 用户提供核心功能执行所需的指令和必要参数。 处理: 按照skill规范执行核心功能执行操作,遵循单一意图原则。 输出: 返回核心功能执行的执行结果,包含操作状态和输出数据。

  • 执行此能力时使用input_params参数,支持创建/查询/导出操作

参数配置与调用

config_options参数进行配置。

输入: 用户提供参数配置与调用所需的指令和必要参数。 处理: 按照skill规范执行参数配置与调用操作,遵循单一意图原则。 输出: 返回参数配置与调用的执行结果,包含操作状态和输出数据。

  • 执行此能力时使用config_options参数,支持修改/重置/导入操作

结果处理与输出

output_format参数进行配置。

输入: 用户提供结果处理与输出所需的指令和必要参数。 处理: 按照skill规范执行结果处理与输出操作,遵循单一意图原则。 输出: 返回结果处理与输出的执行结果,包含操作状态和输出数据。

  • 执行此能力时使用output_format参数,支持导出/保存/转换操作 能力覆盖范围:本skill的核心能力覆盖以下场景关键词:语言陷阱防范与优、选实践指南、覆盖并发、错误处理等核心场、开发者的代码陷阱、防范工具、帮助编写可靠的、陷阱识别与正确使、接口与类型系统陷、阱规避、错误处理优选实践、String、常见陷阱等。这些关键词对应description中声明的使用场景,均已在上述能力点中提供对应的操作支持。

使用场景

  • 不适用: 需要人工判断的复杂决策场景

场景一:并发编程泄漏排查

开发者编写了并发代码,担心 Goroutine 泄漏。

用户:我写了这段并发代码,帮我检查是否有 Goroutine 泄漏

助手:检查以下常见泄漏模式

1. Channel 无发送者 → 泄漏
   // 错误:没有 goroutine 向 ch 发送数据
   func bad() {
       ch := make(chan int)
       go func() {
           <-ch  // 永远阻塞
       }()
   }

2. Context 未检查 → 泄漏
   // 错误:未检查 ctx.Done()
   func bad(ctx context.Context) {
       go func() {
           for {
               // 应该检查 ctx.Done()
               select {
               case <-ctx.Done():
                   return
               default:
                   time.Sleep(time.Second)
               }
           }
       }()
   }

3. 正确模式
   func good(ctx context.Context) {
       ch := make(chan int, 1)
       go func() {
           defer close(ch)
           for {
               select {
               case <-ctx.Done():
                   return
               case ch <- compute():
               }
           }
       }()
   }

场景二:接口类型安全检查

开发者使用接口时遇到 nil 判断异常。

用户:为什么我的接口变量明明是 nil,但判断 i != nil 却是 true

助手:这是 Go 接口的经典陷阱

// 陷阱:nil 具体值放入接口后不为 nil
var p *MyType = nil
var i interface{} = p
fmt.Println(i == nil)  // false!

// 原因:接口包含 (类型, 值) 两个部分
// p 的类型是 *MyType,值是 nil
// 接口 i 的类型不为 nil,所以 i != nil

// 正确做法:直接用 nil 接口
var i interface{} = nil
fmt.Println(i == nil)  // true

// 或在赋值前判断
func process(v interface{}) {
    if v == nil {
        return
    }
    // 使用类型断言
    if p, ok := v.(*MyType); ok && p != nil {
        // 安全使用 p
    }
}

场景三:Slice 共享内存陷阱

开发者修改 Slice 时发现原始数据被意外修改。

// 陷阱:Slice 共享底层数组
func main() {
    original := []int{1, 2, 3, 4, 5}

    // 切片共享内存
    sub := original[1:3]  // [2, 3]
    sub[0] = 99
    fmt.Println(original)  // [1, 99, 3, 4, 5]  原始数据被修改!

    // 正确做法:使用 copy 创建独立副本
    sub2 := make([]int, 2)
    copy(sub2, original[1:3])
    sub2[0] = 99
    fmt.Println(original)  // [1, 2, 3, 4, 5]  原始数据不变

    // Append 可能或可能不重新分配
    appended := append(original, 6)
    appended[0] = 0
    fmt.Println(original)  // 可能被修改也可能不被修改,取决于容量
}

// 安全的 Slice 操作模式
func safeSlice(original []int, start, end int) []int {
    result := make([]int, end-start)
    copy(result, original[start:end])
    return result
}

快速开始

  1. 阅读## 核心能力章节了解skill功能
  2. 按## 依赖说明配置环境
  3. 执行所需能力对应的命令
  4. 参考## 错误处理章节处理异常
  5. 查看## FAQ解答常见疑问

Goroutine 陷阱速查

// 1. 始终确保 Channel 关闭或使用 context
func worker(ctx context.Context, ch <-chan int) {
    for {
        select {
        case <-ctx.Done():
            return
        case val, ok := <-ch:
            if !ok {
                return
            }
            // 处理 val
        }
    }
}

// 2. 使用 WaitGroup 等待所有 goroutine
func processAll(items []int) {
    var wg sync.WaitGroup
    for _, item := range items {
        wg.Add(1)
        go func(i int) {
            defer wg.Done()
            // 处理 i
        }(item)
    }
    wg.Wait()
}

// 3. 使用缓冲 Channel 防止阻塞
func pipeline() {
    ch := make(chan int, 10)  // 适当缓冲
    go func() {
        defer close(ch)
        for i := 0; i < 100; i++ {
            ch <- i
        }
    }()
    for val := range ch {
        // 处理 val
    }
}

Channel 陷阱速查

// 1. 只有发送方关闭 Channel
func producer(ch chan<- int) {
    defer close(ch)  // 发送方负责关闭
    for i := 0; i < 10; i++ {
        ch <- i
    }
}

// 2. 向已关闭 Channel 发送会 panic
func bad(ch chan int) {
    close(ch)
    ch <- 1  // panic: send on closed channel
}

// 3. 从 nil Channel 接收永远阻塞
func nilChannelTrap() {
    var ch chan int  // nil channel
    <-ch  // 永远阻塞
}

// 4. select 多路复用
func selectPattern(ch1, ch2 <-chan int) {
    for {
        select {
        case v1 := <-ch1:
            // 处理 v1
        case v2 := <-ch2:
            // 处理 v2
        }
    }
}

错误处理

  • 边界输入处理: 空输入返回提示信息, 超长输入自动截断
  • 降级策略: 异常时返回默认值, 确保流程不中断 - 处理方式: 按上述步骤操作并确认结果
  • 执行ping命令测试网络连通性,检查防火墙和代理设置连接后重新执行命令机制: 失败时自动执行ping命令测试网络连通性,检查防火墙和代理设置连接后重新执行命令, 最多3次 - 处理方式: 按上述步骤操作并确认结果
// 1. 始终检查 error
func readFile(path string) ([]byte, error) {
    data, err := os.ReadFile(path)
    if err != nil {
        return nil, fmt.Errorf("读取文件失败: %w", err)
    }
    return data, nil
}

// 2. 使用 errors.Is 判断包装的错误
if errors.Is(err, os.ErrNotExist) {
    // 文件不存在
}

// 3. 哨兵错误定义
var ErrNotFound = errors.New("not found")

// 4. 自定义错误类型
type ValidationError struct {
    Field   string
    Message string
}

func (e *ValidationError) Error() string {
    return fmt.Sprintf("%s: %s", e.Field, e.Message)
}

示例

Defer 陷阱速查

// 1. defer 参数立即求值
func deferArgs() {
    i := 1
    defer fmt.Println(i)  // 输出 1,不是 2
    i = 2
}

// 2. 循环中 defer 堆积
func loopDeferTrap() {
    for i := 0; i < 1000000; i++ {
        defer func() {}()  // 所有 defer 在函数结束时才执行
    }
}

// 正确做法:将循环体提取为函数
func loopDeferFixed() {
    for i := 0; i < 1000000; i++ {
        func() {
            defer func() {}()
        }()
    }
}

// 3. 命名返回值与 defer
func namedReturn() (err error) {
    defer func() {
        if err != nil {
            err = fmt.Errorf("包装: %w", err)
        }
    }()
    // ...
    return nil
}

Map 陷阱速查

// 1. nil map 读取返回零值,写入 panic
var m map[string]int
_ = m["key"]  // 返回 0,不 panic
m["key"] = 1  // panic: assignment to entry in nil map

// 正确:使用 make 初始化
m := make(map[string]int)
m["key"] = 1  // OK

// 2. Map 迭代顺序随机
for k, v := range m {
    // 顺序不保证,每次运行可能不同
}

// 3. Map 非并发安全
// 错误:并发读写 map
func concurrentMapBad() {
    m := make(map[int]int)
    go func() {
        m[1] = 1  // 可能 panic
    }()
    go func() {
        _ = m[1]  // 可能 panic
    }()
}

// 正确:使用 sync.Map 或 mutex
var mu sync.RWMutex
m := make(map[int]int)

func set(key, val int) {
    mu.Lock()
    defer mu.Unlock()
    m[key] = val
}

func get(key int) int {
    mu.RLock()
    defer mu.RUnlock()
    return m[key]
}

优选实践

  1. 始终检查 error:忽略 error 是最常见的 Go 缺陷

    if err != nil {
        return err
    }
    
  2. 使用 context 控制 goroutine 生命周期:避免泄漏

  3. 只有发送方关闭 Channel:接收方关闭会导致发送方 panic

  4. Slice 操作用 copy:避免共享内存的意外修改

  5. Map 并发使用 sync.Map 或 mutex:原生 map 非线程安全

  6. defer 用于资源释放:确保文件、连接、锁被释放

  7. 类型断言用 comma-ok 模式:避免 panic

    v, ok := i.(Type)
    

常见问题

Q1:如何检测 Goroutine 泄漏?

// 使用 runtime 查看 goroutine 数量
import "runtime"

fmt.Println(runtime.NumGoroutine())

// 生产环境使用 pprof
import _ "net/http/pprof"

go http.ListenAndServe("localhost:6060", nil)
// 访问 http://localhost:6060/debug/pprof/goroutine

Q2:如何避免 Defer 在循环中堆积?

// 错误
for _, f := range files {
    file, _ := os.Open(f)
    defer file.Close()  // 堆积到函数结束
}

// 正确
for _, f := range files {
    func() {
        file, _ := os.Open(f)
        defer file.Close()
        // 处理文件
    }()
}

Q3:如何正确实现错误包装?

// 使用 %w 包装错误
if err != nil {
    return fmt.Errorf("处理用户 %s 失败: %w", userID, err)
}

// 上游可以使用 errors.Is 判断
if errors.Is(err, sql.ErrNoRows) {
    // 处理未找到
}

Q4:String 长度是字符数还是字节数?

s := "你好"
fmt.Println(len(s))  // 6(字节数,UTF-8 编码每个中文 3 字节)

// 获取字符数
fmt.Println(utf8.RuneCountInString(s))  // 2

// range 遍历的是 rune
for i, r := range s {
    fmt.Printf("%d: %c\n", i, r)
}

Q5:指针接收者还是值接收者?

// 指针接收者:方法可以修改对象状态
func (s *Server) Start() {
    s.running = true
}

// 值接收者:方法不会修改对象
func (s Server) Status() string {
    return s.status
}

// 规则:如果有一个方法用指针接收者,所有方法都应该用指针接收者

Q6:如何安全地关闭 Channel?

// 只有发送方关闭,使用 sync.Once 确保只关闭一次
type SafeChan struct {
    ch   chan struct{}
    once sync.Once
}

func (sc *SafeChan) Close() {
    sc.once.Do(func() {
        close(sc.ch)
    })
}

依赖说明

运行环境

  • Agent 平台: 支持读取 SKILL.md 的任意 AI Agent(Claude Code / Cursor / Codex / Gemini CLI 等)
  • 操作系统: Windows / macOS / Linux
  • Go 版本: 建议 1.18 及以上

依赖详情

依赖项类型是否必需获取方式
Go编译器/运行时必需golang.org 下载
LLM APIAPI必需由 Agent 内置 LLM 提供

API Key 配置

  • 本工具为纯 Markdown 指令驱动,无需额外 API Key
  • Go 模块下载可能需要配置 GOPROXY 代理

可用性分类

  • 分类: MD+EXEC(Markdown 指令 + 命令行执行)
  • 说明: 通过自然语言指令驱动 Agent 提供 Go 代码建议,代码验证需要 Go 编译器执行能力

已知限制

  • 需LLM支持,无LLM环境不可用
  • 复杂业务场景建议结合人工经验判断
  • 执行效率受模型能力与网络环境影响
<!-- 触发条件: 用户明确请求时激活 -->
  • 当前为免费版本,如需完整功能请升级到付费版获取全部能力

评论

加载中…