reflect.copy不是深拷贝工具,它仅逐个赋值元素值,不递归处理指针、结构体或嵌套引用;要求源目标切片类型严格一致、目标可寻址且长度足够,否则panic或截断。

reflect.Copy 不能直接复制切片数据——它只复制元素值,且要求源和目标切片类型完全一致、长度足够,否则 panic 或静默截断。
为什么 reflect.Copy 不是“深拷贝”工具
reflect.Copy 的行为等价于 copy(dst, src) 的反射版:逐个调用 reflect.Value.Set() 赋值,不递归处理指针、结构体字段或嵌套引用。如果切片元素是 struct 指针,复制后两个切片里指向的是同一块内存;如果是包含 map/slice 的 struct,复制的只是浅层字段值。
- 源和目标必须都是
reflect.Slice类型,且元素类型AssignableTo(不是ConvertibleTo) - 目标切片长度决定实际复制数量,不是源切片长度
- 若目标容量不足但长度够,
reflect.Copy仍会写入,不会扩容
正确调用 reflect.Copy 的前提条件
必须确保:dst 是可寻址、可设置的切片;src 是可读切片;二者元素类型严格匹配(比如 []int 和 []int64 就不行)。
src := []string{"a", "b", "c"}
dst := make([]string, 2) // 长度为 2,只会复制前两个
n := reflect.Copy(reflect.ValueOf(dst), reflect.ValueOf(src))
// n == 2,dst == []string{"a", "b"}
- 如果
dst是不可寻址值(如字面量[]string{}),reflect.Copy直接 panic:“reflect: Copy using unaddressable value” - 如果元素类型不兼容,例如
reflect.Copy(reflect.ValueOf([]int{}), reflect.ValueOf([]int64{})),panic:“reflect: Copy: type mismatch” - 目标切片长度为 0,则
n == 0,哪怕源非空
替代方案:什么时候该放弃 reflect.Copy
需要深拷贝、类型转换、或目标切片需动态扩容时,reflect.Copy 不适用。此时应按需选择:
- 已知类型 → 直接用
copy()或append(dst[:0], src...) - 需深拷贝 → 用
github.com/mohae/deepcopy或encoding/gob序列化反序列化(注意性能) - 运行时类型未知但需转成目标切片 → 先
reflect.MakeSlice创建目标,再循环reflect.Index().Set(),比reflect.Copy更可控
比如把任意 []interface{} 转成 []string,reflect.Copy 完全无能为力,必须手动遍历转换。
容易被忽略的 panic 场景
最常踩的坑是传入了不可寻址的 reflect.Value:
slice := []int{1,2,3}
v := reflect.ValueOf(slice) // 这是不可寻址的!
reflect.Copy(v, v) // panic
正确做法是取地址再解引用:
slice := []int{1,2,3}
v := reflect.ValueOf(&slice).Elem() // 可寻址
reflect.Copy(v, v) // OK
-
reflect.ValueOf(x)对局部变量返回不可寻址值;只有&x或reflect.New创建的才可寻址 - 从 map 或 struct 字段取出来的 slice 值默认不可寻址,需先用
Addr().Elem()等方式提升 - 即使类型匹配、长度足够,只要目标不可寻址,
reflect.Copy就 panic,不给任何提示信息











