copy函数仅做浅拷贝,需预分配dst空间,复制元素数取len(dst)与len(src)较小值,多层嵌套或含指针时需手动深拷贝。

copy 函数只做浅拷贝,不是“复制切片”的万能解;它安全高效,但必须配合 make 预分配目标空间,否则什么都不会发生。
目标切片没 make 就调用 copy,结果是空的
这是最常踩的坑:声明一个切片变量但没初始化,就直接 copy(dst, src),返回值是 0,dst 还是 nil 或空切片。
-
dst必须已分配内存 —— 最稳妥写法是dst := make([]T, len(src)) - 如果只写
var dst []int或dst := []int{}(长度为 0),copy实际复制元素数恒为 0 - 错误示例:
var dst []string; copy(dst, []string{"a","b"})→dst仍为空,无 panic 但毫无作用
copy 复制多少个元素?由 len(dst) 和 len(src) 中较小者决定
它不关心你“想复制几个”,只按两个切片当前长度取最小值。这既是灵活性来源,也是隐患点。
- 源长 5、目标长 3 → 只复制前 3 个,后 2 个丢弃(不报错)
- 源长 3、目标长 10 → 只动前 3 个位置,后 7 个保持零值(如
0、""、nil) - 务必检查返回值:
n := copy(dst, src); if n != len(src) { /* 缺失数据,需告警或补逻辑 */ }
多层嵌套结构(如 [][]int、[]*T、[]User)用 copy 会共享底层数据
copy 只复制“当前一层”的值:对 []int 是整数副本,对 [][]int 却只是复制了外层数组里那些 []int 的 header(含指针),内层数组仍共用底层数组。
- 典型翻车:
original := [][]int{{1,2}}; copied := make([][]int, 1); copy(copied, original); copied[0][0] = 999→original[0][0]同样变成 999 - 真要隔离,得手动遍历:
for i := range src { copied[i] = append([]int(nil), src[i]...) } - 结构体切片若含指针字段(如
type User struct { Name *string }),copy仅复制指针值,不新建指向的对象
想彻底断开底层数组?优先用 append([]T(nil), src...)
这个惯用法比 make + copy 更干脆:它强制分配新底层数组,且语义上就是“我要一个新副本”,不易误用。
- 写法简洁:
newSlice := append([]int(nil), oldSlice...) - 效果等价于深拷贝(对纯值类型而言),且避免了
make容量估算失误的风险 - 注意性能权衡:如果后续要频繁
append,它初始容量 =len(oldSlice),可能立即触发扩容;此时可改用dst := make([]int, len(src), cap(src)); copy(dst, src)
真正难的不是调用 copy,而是判断该不该用它 —— 看数据是否含指针、map、slice 或结构体字段;一旦有,就得跳出 copy 思维,手动处理每一层引用关系。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











