← 中文资源

简体中文版本

Go 切片越界 — 差一错误与容量陷阱

根据 panic 给出的上界与容量,核对 len 与 cap,补上边界检查和空输入测试,修复 Go slice bounds out of range。

阅读英文原文

现象

报 panic: runtime error: slice bounds out of range [:5] with capacity 3;正常报文没问题,畸形或较短输入直接崩溃;只在并发或缓冲区复用后出现;堆栈指向重新切片表达式而不是普通下标访问。

诊断

用最小输入复现并查看完整协程栈:

go test ./internal/parse -run TestSplitHeader -v -count=1
GOTRACEBACK=all go test ./internal/parse -run TestSplitHeader -count=1
go test ./... -race -count=1

panic 中的上界与容量说明代码假设了切片至少有 5 个元素,但实际容量只有 3。len(s) 可以小于 cap(s),因此 s[:cap(s)] 合法,却会读到尚未写入的零值区域。GOTRACEBACK=all 能暴露其他同时操作同一缓冲区的协程,-race 可确认是否存在未同步复用。注意 [5:3] 形式表示下界大于上界,也是同一类错误。

修复

在切片前校验长度,用半开区间并保证不超过 len;需要截断时使用 Go 1.21 的内置 min 或 slices 包。输入过短应返回错误而不是静默补零:

parts := bytes.SplitN(line, []byte(":"), 2)
if len(parts) != 2 {
    return "", fmt.Errorf("malformed header: %q", line)
}
return strings.TrimSpace(string(parts[1])), nil

如果确实需要按容量扩容,先用 append 写入再按实际长度切片。用 recover 吞掉 panic 会让进程继续在错误状态下运行,不应作为修复手段。

验证

补充空输入、单元素、恰好边界和少一个元素的表驱动测试,并在 -race 下跑完整测试集。panic 应被明确的错误信息取代,合法输入结果不变;若切片来自对象池,复用前显式重置长度,避免下一个调用方继承过期边界。