slices包是Go标准库中已稳定的泛型切片操作集合,slices.Sort原地修改底层数组,slices.Contains只读不写,slices.Delete仅调整len/cap而不清零元素,唯slices.Clone真正隔离内存。

slices 包不是“教程工具”,而是标准库中已稳定、可直接用的泛型切片操作集合。它不依赖反射,不牺牲性能,但若你没理解切片底层数组共享机制,slices.Clone 以外的多数函数都可能引发意料外的副作用。
为什么 slices.Sort 会改原切片,而 slices.Contains 不会
这取决于函数是否需要重排、移动或覆盖底层数组中的元素:
- slices.Sort 是原地排序,直接修改底层数组内容,所以影响所有共享该数组的切片
- slices.Contains 只读遍历,不写入,自然无副作用
- slices.Compact 和 slices.Delete 看似“返回新切片”,实则仍复用原底层数组 —— 它们只是调整了返回切片的 len 和 ptr,未分配新内存
- 唯一真正隔离内存的常用函数是 slices.Clone,它内部调用 make + copy
slices.Delete 的坑:删完还能访问“被删位置”的值
这是最常被误判的行为。例如:
s := []int{1, 2, 3, 4, 5}
s = slices.Delete(s, 2, 3) // 删除索引 [2,3),即删掉 3
fmt.Println(s) // [1 2 4 5]
fmt.Println(len(s), cap(s)) // len=4, cap=5 —— 底层数组没变!
fmt.Println(&s[3], &s[2]) // 地址连续,说明 4 和 5 还在原数组里
这意味着:
- 若原始底层数组还被其他变量引用(比如从数组截取而来),删掉的元素仍可通过其他切片访问
- 若切片含指针或大结构体,被“逻辑删除”但未清零的元素会阻碍 GC 回收
- 正确做法:如需彻底清除,应在 slices.Delete 后手动置零,例如 s[3] = 0(对基础类型)或 for i := start; i
哪些函数必须注意类型约束?constraints.Ordered vs comparable
编译失败往往卡在这一步。关键看函数行为:
- 需要比较大小(, <code>>)的函数,如 slices.Sort、slices.BinarySearch,要求 T constraints.Ordered —— string、int、float64 可以,但自定义 struct 默认不行
- 只需相等判断(==)的函数,如 slices.Contains、slices.Equal、slices.Index,只要求 T comparable —— 大部分 struct 只要字段都可比较,就满足条件
- 若传入自定义类型报错,不要硬加 constraints.Ordered;先确认它是否真需要排序;否则实现 Compare 方法或用 sort.Slice + 自定义 less 函数更稳妥
slices.Clone 不是万能解药:它只克隆当前长度,不保留容量
很多人以为 slices.Clone 能完全复制一个切片的“状态”,其实它只做一件事:
- 分配一块新底层数组,长度 = 原切片 len,容量 = len(不是原 cap)
- 把原切片的 len 个元素拷过去
所以:
- 若你依赖高容量避免后续
append扩容(比如预分配了make([]T, 0, 1024)),slices.Clone后再append会立刻触发扩容 - 若需保留原容量,得手写:
dst := make([]T, len(src), cap(src)); copy(dst, src) -
slices.Clone对nil切片安全,返回仍是nil;但对空切片([]T{})返回非nil切片,这点在 JSON 序列化时要注意
真正麻烦的从来不是“怎么调用函数”,而是调用后底层数组是否还在被谁悄悄读写。泛型没改变切片的本质 —— 它还是那个带指针、长度和容量的三元组。


















