copy函数返回实际拷贝的元素个数,即len(dst)与len(src)的最小值;它不是布尔值或错误码,不表示成功与否,仅反映本次操作量。

copy 函数返回值就是实际拷贝的元素个数
它不是布尔值,也不是错误码,就是一个 int —— 你传进去的两个切片中,较短那个的长度。比如 copy(dst[2:5], src[0:10]),dst 可用空间是 3 个元素(索引 2 到 4),src 有 10 个,那返回值就是 3。
常见错误现象:copy(dst, src) 返回 0,但 dst 看起来“没变”——其实不是没拷,而是 dst 是 nil 或长度为 0 的空切片,或者 src 是空的。
- 如果
dst是nil,copy直接返回0,不 panic - 如果
dst长度为 0(比如[]int{}),也返回0 - 如果
src是空切片,同样返回0 - 返回值永远 ≤
len(dst),也 ≤len(src),取二者最小值
为什么不能靠返回值判断“是否成功”,而要看是否为 0
copy 没有失败概念:只要参数类型匹配、内存可访问,它就一定“执行完”,只是可能一个元素都没拷。所以返回值不是 success/fail 信号,而是“我干了多少”的计量器。
使用场景:常用于流式读写、缓冲区填充、分块拷贝。比如从网络读到 buf,再拷进目标切片,你需要知道这次真正用了多少字节,来推进偏移量或判断是否读满。
立即学习“go语言免费学习笔记(深入)”;
- 别写
if copy(dst, src) != nil—— 返回值是int,不能和nil比 - 别默认“返回非 0 就代表全量拷完”,它只反映本次操作的上限
- 要判断是否“预期数据全部落进 dst”,得自己比对
len(src)和返回值
copy(dst, src) 中 dst 和 src 类型必须严格一致
Go 的 copy 不做类型转换。哪怕都是 []byte 和 []uint8,也不能混用——因为它们底层虽同构,但类型不同。
常见错误现象:cannot use src (type []uint8) as type []byte in argument to copy。这不是语法糖缺失,是类型系统在拦你。
-
[]byte和[]uint8在 Go 中是**不同类型**,即使reflect.TypeOf显示一样 - 字符串转切片要用
[]byte(s),而不是[]uint8(s) - 如果 src 是
string,dst 是[]byte,这是合法的;反过来(dst 是 string)不行,因为 string 是只读的 - 结构体切片之间不能互相
copy,哪怕字段完全一样
性能与底层数组共享关系:copy 不影响底层数组引用
copy 做的是内存复制(memmove),不是指针赋值。哪怕 dst 和 src 共享同一底层数组,拷贝后也是独立副本。
容易踩的坑:以为 copy(a[1:], a) 是安全的“整体左移”,其实它是未定义行为(重叠拷贝),Go 运行时不会报错,但结果取决于底层实现,可能出错也可能侥幸通过。
- Go 的
copy对重叠区域行为是“类似 memmove”,但你不该依赖它——显式重叠属于未文档化行为 - 需要原地移动,请用循环或
append+ 切片重组,比如a = append(a[1:], a[0]) - 跨 goroutine 写同一个底层数组?
copy本身不加锁,得你自己同步 - 大量小拷贝(如每次 1 字节)会放大函数调用和边界检查开销,不如预分配 buffer 后批量处理
最常被忽略的一点:很多人把 copy 当成“赋值替代品”,比如想让 dst = src,结果写了 copy(dst, src) 却忘了 dst 长度不够——这时返回值是 len(dst),但业务逻辑可能误以为“全量同步完成了”。










