---
id: daily-golang-safety
name: "golang-safety"
url: https://skills.yangsir.net/skill/daily-golang-safety
author: samber
domain: ai-code-generation-quality
tags: ["defensive-programming", "error-handling", "code-safety", "runtime-errors", "bug-prevention"]
install_count: 32600
rating: 4.60 (30 reviews)
github: https://github.com/samber/cc-skills-golang
---

# golang-safety

> 预防 Go 代码中的 nil 指针、数组越界、整数溢出等运行时错误，自动添加防御性检查

**Stats**: 32,600 installs · 4.6/5 (30 reviews)

## Before / After 对比

### 运行时错误预防

**Before**:

手动防御性编程需要检查所有可能的 nil、越界和溢出场景，代码量增加 30-50%，且容易遗漏边界情况，一个函数需要 10-15 分钟

**After**:

自动分析数据流和输入边界，生成精确的防御性检查代码，2 分钟完成一个函数的安全加固，覆盖所有异常路径

| Metric | Before | After | Change |
|---|---|---|---|
| 加固时间 | 12分钟 | 2分钟 | -83% |

## Readme

# golang-safety

**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

- **Prefer generics over `any`** when the type set is known — compiler catches mismatches instead of runtime panics

- **Always use comma-ok for type assertions** — bare assertions panic on mismatch

- **Typed nil pointer in an interface is not `== nil`** — the type descriptor makes it non-nil

- **Writing to a nil map panics** — always initialize before use

- **`append` may reuse the backing array** — both slices share memory if capacity allows, silently corrupting each other

- **Return defensive copies** from exported functions — otherwise callers mutate your internals

- **`defer` runs at function exit, not loop iteration** — extract loop body to a function

- **Integer conversions truncate silently** — `int64` to `int32` wraps without error

- **Float arithmetic is not exact** — use epsilon comparison or `math/big`

- **Design useful zero values** — nil map fields panic on first write; use lazy init

- **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
Read from nil
Write to nil
Len/Cap of nil
Range over nil

Map
Zero value
**panic**
0
0 iterations

Slice
**panic** (index)
**panic** (index)
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](https://github.com/samber/cc-skills-golang/blob/HEAD/skills/golang-safety/./references/nil-safety.md)** 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](https://github.com/samber/cc-skills-golang/blob/HEAD/skills/golang-safety/./references/slice-map-safety.md)** 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
0.1+0.2 == 0.3 // false

// ✓ Good — use epsilon comparison
const epsilon = 1e-9
math.Abs((0.1+0.2)-0.3) < 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-linter` skill for configuration and usage.

## 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

Weekly Installs737Repository[samber/cc-skills-golang](https://github.com/samber/cc-skills-golang)GitHub Stars1.1KFirst SeenMar 22, 2026Security Audits[Gen Agent Trust HubPass](/samber/cc-skills-golang/golang-safety/security/agent-trust-hub)[SocketPass](/samber/cc-skills-golang/golang-safety/security/socket)[SnykPass](/samber/cc-skills-golang/golang-safety/security/snyk)Installed onopencode701cursor696codex692gemini-cli690github-copilot689amp688

---
*Source: https://skills.yangsir.net/skill/daily-golang-safety*
*Markdown mirror: https://skills.yangsir.net/api/skill/daily-golang-safety/markdown*