判断切片是否为nil的唯一可靠方式是s == nil;len(s) == 0无法区分nil与空切片;interface{}或泛型中需用反射判断;json序列化时nil转null、空切片转[],语义不同。

怎么判断一个切片是 nil 还是空?
唯一可靠、符合 Go 习惯的判断方式就是 s == nil。别信 len(s) == 0 —— 它对两者都返回 true,完全无法区分。
-
var s []int→s == nil是true -
s := []int{}或s := make([]int, 0)→s == nil恒为false - 如果变量类型是
interface{}或泛型参数(如func IsNil[T any](v T)),v == nil会编译失败;此时必须用反射:reflect.ValueOf(v).Kind() == reflect.Slice && reflect.ValueOf(v).IsNil()
JSON 序列化时为什么一个变 null、一个变 []?
因为 json.Marshal 把语义当真:nil 切片表示“字段未设置/不存在”,空切片表示“明确设置了,但内容为空”。这在 API 兼容性上是高频翻车点。
-
json.Marshal(struct{ Data []string }{Data: nil})→{"Data":null} -
json.Marshal(struct{ Data []string }{Data: []string{}})→{"Data":[]} - 前端传
{"tags": null}和{"tags": []},Go 解析后分别是nil和非nil切片 —— 若业务逻辑只检查len(tags) == 0,就会漏掉“客户端根本没传这个字段”的情况
append 时它们表现一样,那还用区分吗?
绝大多数时候 append 确实都安全,但“表现一样”不等于“底层一样”,容易在扩容策略和调试中埋坑。
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
-
append(nilSlice, x)总是分配新底层数组(cap=1 起步) -
append(emptySlice, x)可能复用底层数组(如果 cap > 0),但[]int{}的 cap 是 0,所以首次 append 也必然分配 - 想预分配容量?用
make([]int, 0, 10),不是[]int{}—— 后者永远 cap=0,后续每次 append 都可能触发 realloc - 别写
if cap(s) 这类逻辑:对 nil 切片,<code>cap(s)是 0,条件恒成立,纯属多余分配
函数参数或结构体字段该初始化成哪个?
取决于你想表达什么语义,不是性能问题,而是契约信号。
- 想表达“这个字段可选,没传就是没这个概念”,用
var s []T(即零值 nil) - 想表达“这个容器已准备好,只是当前没数据”,用
s := []T{}或s := make([]T, 0) - 标准库倾向前者:
json.Unmarshal把 JSON 中的null解成 nil 切片,把[]解成空切片;如果你的结构体字段定义为Data []string,就天然支持这种区分
最麻烦的地方在于:90% 的操作里它们真的一样,只有 JSON、反射、ORM 映射、C 交互等少数场景才暴露差异 —— 等你踩到,往往已是线上 bug。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










