nil切片是未初始化的切片,底层数组指针、len、cap均为0,s == nil为true;空切片已初始化,底层数组指针非nil,len和cap为0,s == nil恒为false。

声明 nil 切片:变量本身为 nil,未分配底层数组
nil 切片的 len 和 cap 都是 0,但它指向的底层数组指针为 nil。它和未初始化的切片变量等价,常用于表示“尚未创建”或“无数据”的语义。
常见错误是误以为 var s []int 是空切片(实际就是 nil),或试图对它做 append 后不检查结果——其实可以,Go 允许向 nil 切片 append,会自动分配底层数组。
-
var s []int—— 最标准的 nil 切片声明 -
var s []string = nil—— 显式赋 nil,效果相同 -
s := []int(nil)—— 类型转换写法,不推荐,易混淆 - 判断是否为 nil:用
s == nil;注意len(s) == 0不能区分 nil 和空切片
声明空切片:有底层数组,但长度为 0
空切片的 len 和 cap 均为 0,但底层数组指针非 nil(通常指向一个零长的全局数组)。它已分配内存结构,只是当前没元素。
典型场景是明确需要一个“可立即追加、且不触发首次扩容”的切片,比如预分配容量后清空再复用;或者测试中要构造一个非-nil但无元素的值。
立即学习“go语言免费学习笔记(深入)”;
-
s := []int{}—— 字面量方式,最常用 -
s := make([]int, 0)—— 指定长度为 0,cap 默认也为 0 -
s := make([]int, 0, 16)—— 指定 cap=16,后续 append 前 16 次不扩容 - 判断是否为空:用
len(s) == 0;但它!= nil
nil 切片 vs 空切片:传参和 JSON 序列化的差异
两者在大多数操作(如遍历、append)中行为一致,但关键区别在序列化和接口比较上。
- JSON 编码:
json.Marshal(nil切片)输出null;json.Marshal(空切片)输出[] - 接口值比较:
interface{}(nil切片) == interface{}(nil切片)为 true;但interface{}(空切片) == interface{}(空切片)不一定为 true(底层指针可能不同) - 函数参数接收:如果函数内部要做
s == nil判断(例如做懒初始化),传入[]int{}就会跳过该逻辑 - 反射判断:
reflect.ValueOf(s).IsNil()对 nil 切片返回 true,对空切片返回 false
什么时候该用哪个?看语义,不是看长度
别只盯着 len 是不是 0。真正决定选型的是你想表达什么。
- 想表示“数据不存在/未加载/无效”,用 nil 切片(如数据库查询无结果,返回
nil而非空 slice) - 想表示“数据存在但当前为空”,用空切片(如用户暂无订单,返回
[]Order{}) - API 返回值需保持 JSON 一致性时,必须明确区分:前端依赖
[]表示“有这个字段”,null表示“字段缺失” - 性能敏感路径中,若确定后续必 append 若干元素,优先用
make([]T, 0, N)避免多次扩容
最容易被忽略的是:nil 切片在结构体中不会被 JSON 包含(omitempty 下),而空切片会作为 [] 出现——这经常导致前后端联调时字段存在性对不上。


















