Agent Skillssamber/cc-skills-golang › golang-safety

golang-safety

GitHub

提供Go语言防御性编程指南,防止panic、数据损坏和运行时bug。涵盖nil安全、类型断言、并发访问及资源生命周期等最佳实践,用于代码审查与编写规范。

skills/golang-safety/SKILL.md samber/cc-skills-golang

Trigger Scenarios

遭遇nil panic或map并发访问问题 审查Go代码中的nil安全性 处理slice/map防御性拷贝 设计零值或资源生命周期管理

Install

npx skills add samber/cc-skills-golang --skill golang-safety -g -y
More Options

Use without installing

npx skills use samber/cc-skills-golang@golang-safety

指定 Agent (Claude Code)

npx skills add samber/cc-skills-golang --skill golang-safety -a claude-code -g -y

安装 repo 全部 skill

npx skills add samber/cc-skills-golang --all -g -y

预览 repo 内 skill

npx skills add samber/cc-skills-golang --list

SKILL.md

Frontmatter
{
    "name": "golang-safety",
    "license": "MIT",
    "metadata": {
        "author": "samber",
        "version": "1.2.1",
        "openclaw": {
            "emoji": "🛡",
            "install": [],
            "homepage": "https:\/\/github.com\/samber\/cc-skills-golang",
            "requires": {
                "bins": [
                    "go"
                ]
            }
        }
    },
    "description": "Defensive Golang coding to prevent panics, silent data corruption, and subtle runtime bugs. Use when encountering nil panics, append aliasing, map concurrent access, float comparison pitfalls, or zero-value design questions. Also use when reviewing code for nil-safety, numeric conversion overflow, resource lifecycle issues (defer in loops), or defensive copying of slices and maps.",
    "allowed-tools": "Read Edit Write Glob Grep Bash(go:*) Bash(golangci-lint:*) Bash(git:*) Agent",
    "compatibility": "Designed for Claude Code or similar AI coding agents, and for projects using Golang.",
    "user-invocable": true
}

Persona: You are a defensive Go engineer. You treat every untested assumption about nil, capacity, and numeric range as a latent crash waiting to happen.

Go Safety: Correctness & Defensive Coding

Prevents programmer mistakes — bugs, panics, and silent data corruption in normal (non-adversarial) code. Security handles attackers; safety handles ourselves.

Best Practices Summary

  1. Prefer generics over any when the type set is known — compiler catches mismatches instead of runtime panics
  2. Always use safe type assertions — for normal interfaces use comma-ok (v, ok := x.(T)); for reflection in Go 1.25+ prefer reflect.TypeAssert[T](value) over value.Interface().(T).
  3. Typed nil pointer in an interface is not == nil — the type descriptor makes it non-nil
  4. Writing to a nil map panics — always initialize before use
  5. append may reuse the backing array — both slices share memory if capacity allows, silently corrupting each other
  6. Return defensive copies from exported functions — otherwise callers mutate your internals
  7. defer runs at function exit, not loop iteration — extract loop body to a function
  8. Integer conversions truncate silentlyint64 to int32 wraps without error
  9. Float arithmetic is not exact — use epsilon comparison or math/big
  10. Design useful zero values — nil map fields panic on first write; use lazy init
  11. Use sync.Once for lazy init — guarantees exactly-once even under concurrency

Nil Safety

Nil-related panics are the most common crash in Go.

The nil interface trap

Interfaces store (type, value). An interface is nil only when both are nil. Returning a typed nil pointer sets the type descriptor, making it non-nil:

// ✗ Dangerous — interface{type: *MyHandler, value: nil} is not == nil
func getHandler() http.Handler {
    var h *MyHandler // nil pointer
    if !enabled {
        return h // interface{type: *MyHandler, value: nil} != nil
    }
    return h
}

// ✓ Good — return nil explicitly
func getHandler() http.Handler {
    if !enabled {
        return nil // interface{type: nil, value: nil} == nil
    }
    return &MyHandler{}
}

Nil map, slice, and channel behavior

Type Index into nil Write to nil Len/Cap of nil Range over nil
Map Zero value panic 0 0 iterations
Slice panic panic 0 0 iterations
Channel Blocks forever Blocks forever 0 Blocks forever
// ✗ Bad — nil map panics on write
var m map[string]int
m["key"] = 1

// ✓ Good — initialize or lazy-init in methods
m := make(map[string]int)

func (r *Registry) Add(name string, val int) {
    if r.items == nil { r.items = make(map[string]int) }
    r.items[name] = val
}

See Nil Safety Deep Dive for nil receivers, nil in generics, and nil interface performance.

Slice & Map Safety

Slice aliasing — the append trap

append reuses the backing array if capacity allows. Both slices then share memory:

// ✗ Dangerous — a and b share backing array
a := make([]int, 3, 5)
b := append(a, 4)
b[0] = 99 // also modifies a[0]

// ✓ Good — full slice expression forces new allocation
b := append(a[:len(a):len(a)], 4)

Map concurrent access

Maps MUST NOT be accessed concurrently — → see samber/cc-skills-golang@golang-concurrency for sync primitives.

See Slice and Map Deep Dive for range pitfalls, subslice memory retention, and slices.Clone/maps.Clone.

Numeric Safety

Implicit type conversions truncate silently

// ✗ Bad — silently wraps around if val > math.MaxInt32 (3B becomes -1.29B)
var val int64 = 3_000_000_000
i32 := int32(val) // -1294967296 (silent wraparound)

// ✓ Good — check before converting
if val > math.MaxInt32 || val < math.MinInt32 {
    return fmt.Errorf("value %d overflows int32", val)
}
i32 := int32(val)

Float comparison

// ✗ Bad — floating point arithmetic is not exact
var a, b, c float64 = 0.1, 0.2, 0.3
a+b == c // false

// ✓ Good — use epsilon comparison
const epsilon = 1e-9
math.Abs((a+b)-c) < epsilon // true

Division by zero

Integer division by zero panics. Float division by zero produces +Inf, -Inf, or NaN.

func avg(total, count int) (int, error) {
    if count == 0 {
        return 0, errors.New("division by zero")
    }
    return total / count, nil
}

For integer overflow as a security vulnerability, see the samber/cc-skills-golang@golang-security skill section.

Resource Safety

defer in loops — resource accumulation

defer runs at function exit, not loop iteration. Resources accumulate until the function returns:

// ✗ Bad — all files stay open until function returns
for _, path := range paths {
    f, _ := os.Open(path)
    defer f.Close() // deferred until function exits
    process(f)
}

// ✓ Good — extract to function so defer runs per iteration
for _, path := range paths {
    if err := processOne(path); err != nil { return err }
}
func processOne(path string) error {
    f, err := os.Open(path)
    if err != nil { return err }
    defer f.Close()
    return process(f)
}

Goroutine leaks

→ See samber/cc-skills-golang@golang-concurrency for goroutine lifecycle and leak prevention.

Immutability & Defensive Copying

Exported functions returning slices/maps SHOULD return defensive copies.

Protecting struct internals

// ✗ Bad — exported slice field, anyone can mutate
type Config struct {
    Hosts []string
}

// ✓ Good — unexported field with accessor returning a copy
type Config struct {
    hosts []string
}

func (c *Config) Hosts() []string {
    return slices.Clone(c.hosts)
}

Initialization Safety

Zero-value design

Design types so var x MyType is safe — prevents "forgot to initialize" bugs:

var mu sync.Mutex   // ✓ usable at zero value
var buf bytes.Buffer // ✓ usable at zero value

// ✗ Bad — nil map panics on write
type Cache struct { data map[string]any }

sync.Once for lazy initialization

type DB struct {
    once sync.Once
    conn *sql.DB
}

func (db *DB) connection() *sql.DB {
    db.once.Do(func() {
        db.conn, _ = sql.Open("postgres", connStr)
    })
    return db.conn
}

init() function pitfalls

→ See samber/cc-skills-golang@golang-design-patterns for why init() should be avoided in favor of explicit constructors.

Enforce with Linters

Many safety pitfalls are caught automatically by linters: errcheck, forcetypeassert, nilerr, govet, staticcheck. See the samber/cc-skills-golang@golang-lint skill for configuration and usage.

Go 1.25+ reflection type assertions

For reflection code, prefer reflect.TypeAssert[T] over value.Interface().(T).

v := reflect.ValueOf(x)
if s, ok := reflect.TypeAssert[string](v); ok {
    use(s)
}

Cross-References

  • → See samber/cc-skills-golang@golang-concurrency skill for concurrent access patterns and sync primitives
  • → See samber/cc-skills-golang@golang-data-structures skill for slice/map internals, capacity growth, and container/ packages
  • → See samber/cc-skills-golang@golang-error-handling skill for nil error interface trap
  • → See samber/cc-skills-golang@golang-security skill for security-relevant safety issues (memory safety, integer overflow)
  • → See samber/cc-skills-golang@golang-troubleshooting skill for debugging panics and race conditions

Common Mistakes

Mistake Fix
Bare type assertion v := x.(T) Panics on type mismatch, crashing the program. Use v, ok := x.(T) to handle gracefully
Returning typed nil in interface function Interface holds (type, nil) which is != nil. Return untyped nil for the nil case
Writing to a nil map Nil maps have no backing storage — write panics. Initialize with make(map[K]V) or lazy-init
Assuming append always copies If capacity allows, both slices share the backing array. Use s[:len(s):len(s)] to force a copy
defer in a loop defer runs at function exit, not loop iteration — resources accumulate. Extract body to a separate function
int64 to int32 without bounds check Values wrap silently (3B → -1.29B). Check against math.MaxInt32/math.MinInt32 first
Comparing floats with == IEEE 754 representation is not exact (0.1+0.2 != 0.3). Use math.Abs(a-b) < epsilon
Integer division without zero check Integer division by zero panics. Guard with if divisor == 0 before dividing
Returning internal slice/map reference Callers can mutate your struct's internals through the shared backing array. Return a defensive copy
Multiple init() with ordering assumptions init() execution order across files is unspecified. → See samber/cc-skills-golang@golang-design-patterns — use explicit constructors
Blocking forever on nil channel Nil channels block on both send and receive. Always initialize before use

Cross-References

  • → See samber/cc-skills-golang@golang-continuous-integration skill for automated AI-driven code review in CI using these guidelines

Version History

  • 709b181 Current 2026-07-25 07:36

Same Skill Collection

skills/golang-code-style/SKILL.md
skills/golang-concurrency/SKILL.md
skills/golang-context/SKILL.md
skills/golang-data-structures/SKILL.md
skills/golang-database/SKILL.md
skills/golang-dependency-management/SKILL.md
skills/golang-design-patterns/SKILL.md
skills/golang-documentation/SKILL.md
skills/golang-graphql/SKILL.md
skills/golang-grpc/SKILL.md
skills/golang-lint/SKILL.md
skills/golang-modernize/SKILL.md
skills/golang-popular-libraries/SKILL.md
skills/golang-project-layout/SKILL.md
skills/golang-samber-do/SKILL.md
skills/golang-samber-hot/SKILL.md
skills/golang-samber-mo/SKILL.md
skills/golang-samber-oops/SKILL.md
skills/golang-samber-slog/SKILL.md
skills/golang-security/SKILL.md
skills/golang-stay-updated/SKILL.md
skills/golang-stretchr-testify/SKILL.md
skills/golang-uber-dig/SKILL.md
skills/golang-uber-fx/SKILL.md
skills/golang-benchmark/SKILL.md
skills/golang-cli/SKILL.md
skills/golang-continuous-integration/SKILL.md
skills/golang-dependency-injection/SKILL.md
skills/golang-error-handling/SKILL.md
skills/golang-google-wire/SKILL.md
skills/golang-gopls/SKILL.md
skills/golang-how-to/SKILL.md
skills/golang-naming/SKILL.md
skills/golang-observability/SKILL.md
skills/golang-performance/SKILL.md
skills/golang-pkg-go-dev/SKILL.md
skills/golang-refactoring/SKILL.md
skills/golang-samber-lo/SKILL.md
skills/golang-samber-ro/SKILL.md
skills/golang-spf13-cobra/SKILL.md
skills/golang-spf13-viper/SKILL.md
skills/golang-structs-interfaces/SKILL.md
skills/golang-swagger/SKILL.md
skills/golang-testing/SKILL.md
skills/golang-troubleshooting/SKILL.md

Metadata

Files
0
Version
30cdf15
Hash
738d483e
Indexed
2026-07-25 07:36

inicio - Wiki
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-08-20 05:03
浙ICP备14020137号-1 $mapa de visitantes$