copy函数仅逐元素复制已分配底层数组的目标切片,要求目标非nil且长度足够;否则静默失败;安全做法是make([]t, len(src))后copy,并校验返回值;对[]byte需注意[:n]边界;它只做浅拷贝,引用类型仍共享底层;“复制并扩容”推荐append(nil, s...)或预分配容量的make+copy。

copy 函数不是“复制切片”的快捷键,它只是把源切片的元素逐个写进目标切片已分配的底层数组里。目标切片必须存在、长度足够,否则什么都不会发生——连 panic 都不会触发。
目标切片没初始化或长度太小,copy 就是白忙活
常见错误:声明但不初始化,或长度配错。
-
var dst []int→dst是nil,len(dst) == 0,copy(dst, src)返回0 -
dst := make([]int, 2)但src有 5 个元素 → 只拷前 2 个,返回值是2,不是5 - 误用
make([]int, 0, len(src))→ 长度为 0,照样拷不了;得用make([]int, len(src))
安全写法永远是:dst := make([]T, len(src)); n := copy(dst, src); if n != len(src) { /* 处理截断 */ }
copy 对 []byte 最常用,但别漏掉 [:n] 边界
网络读取、JSON 解析后备份原始数据时,copy 是主力,但容易在边界上翻车。
- 从
conn.Read(buf)拿到n, err := conn.Read(buf)后,要拷的是buf[:n],不是整个buf - 写成
copy(dst, buf)会把未读部分的垃圾值也拷过去(比如上次残留的零值) -
copy(dst, []byte(s))可行,但每次调用都新分配内存;若频繁操作,建议复用dst并用copy(dst, []byte(s))+dst[:len(s)]
浅拷贝陷阱:结构体、指针、嵌套切片全都不免疫
copy 不管内容是什么类型,只做内存块级复制。值类型安全,引用类型共享底层。
-
[][]int:外层切片被复制,内层数组仍共用 → 改copied[0][0]会同步改original[0][0] -
[]*int:指针值被复制,两个切片里的指针指向同一地址 -
[]User,其中User含map[string]int字段 →mapheader 被复制,底层哈希表仍是同一个
真需要隔离,必须手动遍历重建:对每个元素 newVal := *oldVal 或调用 Clone() 方法,不能依赖 copy。
想“复制并扩容”,别硬套 copy,用 append 更自然
如果目标是“拿到一个独立副本,后续还要追加”,copy + make 不是最优解。
-
newSlice := append([]T(nil), oldSlice...)—— 简洁、语义清晰、保证底层数组全新 - 但注意:若
oldSlice容量很大(比如从大数组截取),append(nil, ...)不会复用旧底层数组,一定新分配 - 若后续要高频
append,且已知最终容量,更高效的是:dst := make([]T, len(oldSlice), cap(oldSlice)*2); copy(dst, oldSlice)
真正容易被忽略的点是:即使你用 make 分配了新底层数组,只要目标切片本身是从别的切片截取来的(比如 src[10:20]),它的底层数组可能仍和原始大数组绑定——make 的对象必须是全新构造的,不能是子切片。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











