Go标准库未提供reflect.DeepCopy是设计使然,因深拷贝语义(如指针处理、字段跳过、循环引用)需用户定义;反射仅暴露结构信息,不负责内存复制逻辑。
为什么 reflect.DeepCopy 不存在
Go 标准库没有提供 reflect.DeepCopy 函数,也不是遗漏——而是设计上刻意回避。反射本身不负责内存复制逻辑,reflect 包只暴露类型和值的结构信息,真正的拷贝行为必须由用户定义语义:是否复制指针指向的内容?是否跳过某些字段(如 sync.Mutex)?是否处理循环引用?这些都无法由反射自动推断。
用 reflect.Value.Set + 递归实现基础深拷贝
核心思路是:对源值递归遍历每个字段或元素,为每个目标位置创建新实例,再用 reflect.Value.Set 赋值。但必须注意类型可寻址性、不可设置字段(如未导出字段、func、unsafe.Pointer)、以及非复合类型的直接复制。
常见错误现象:panic: reflect: reflect.Value.Set using unaddressable value —— 源值不是地址,或目标值未通过 reflect.New 或 reflect.MakeSlice 等获取可寻址副本。
- 只处理导出字段(未导出字段无法通过反射写入,强行访问会 panic)
- 遇到
nil指针、切片、map,需先用reflect.New/reflect.MakeSlice/reflect.MakeMap初始化目标 - 对
interface{}类型需先取底层值(v.Elem()),再递归处理 - 原始类型(
int、string、bool)直接Set即可,无需额外分配
// 示例:简单结构体深拷贝(无嵌套未导出字段、无循环引用)
func deepCopy(src interface{}) interface{} {
v := reflect.ValueOf(src)
if !v.IsValid() {
return nil
}
dst := reflect.New(v.Type()).Elem()
copyValue(v, dst)
return dst.Interface()
}
func copyValue(src, dst reflect.Value) {
switch src.Kind() {
case reflect.Ptr:
if src.IsNil() {
dst.Set(reflect.Zero(dst.Type()))
return
}
dstPtr := reflect.New(src.Elem().Type())
copyValue(src.Elem(), dstPtr.Elem())
dst.Set(dstPtr)
case reflect.Struct:
for i := 0; i < src.NumField(); i++ {
if src.Type().Field(i).IsExported() {
copyValue(src.Field(i), dst.Field(i))
}
}
case reflect.Slice, reflect.Array:
newSlice := reflect.MakeSlice(dst.Type(), src.Len(), src.Cap())
for i := 0; i < src.Len(); i++ {
copyValue(src.Index(i), newSlice.Index(i))
}
dst.Set(newSlice)
case reflect.Map:
newMap := reflect.MakeMap(dst.Type())
for _, key := range src.MapKeys() {
val := reflect.New(src.MapIndex(key).Type()).Elem()
copyValue(src.MapIndex(key), val)
newMap.SetMapIndex(key, val)
}
dst.Set(newMap)
default:
dst.Set(src)
}
}
什么时候不该用反射做深拷贝
反射深拷贝性能差、易出错、难以调试,仅适用于「类型不确定且无法预知结构」的极少数场景。大多数情况下应优先选择: