最快上手但有坑:json.marshal/unmarshal仅处理导出字段,遇func/chan/unsafe.pointer或未导出字段会panic或丢弃;适合纯数据dto;含time.time需自定义marshaler;copier支持深拷贝未导出字段但需显式选项;手写clone()最稳可控。

用 json.Marshal + json.Unmarshal 最快上手但有坑
结构体里含 func、chan、unsafe.Pointer 或未导出字段时,json.Marshal 直接 panic 或静默丢字段。它只处理导出(首字母大写)字段,且要求所有嵌套类型可序列化。
适合场景:配置结构体、纯数据 DTO、临时调试拷贝,不涉及方法或私有状态。
- 必须确保目标结构体所有字段可被 JSON 编码(比如不能有
map[interface{}]interface{}) - 时间字段会变成字符串,再反解可能丢失精度或时区信息
- 如果结构体含
time.Time,建议先注册自定义json.Marshaler实现
var src MyStruct = MyStruct{ID: 1, Name: "test"}
data, _ := json.Marshal(src)
var dst MyStruct
json.Unmarshal(data, &dst) // 注意传指针
github.com/jinzhu/copier 能绕过反射限制但依赖第三方
这个库用代码生成+反射组合,在保持易用性的同时支持深拷贝未导出字段(需开启 copier.CopyWithOption)、忽略字段、类型转换等。比纯 reflect 手写安全,也比 json 方案更可控。
常见错误:没加 copier.DeepCopy 选项,默认是浅拷贝;或者忘记给目标变量分配内存(dst 是 nil 指针时直接 panic)。
立即学习“go语言免费学习笔记(深入)”;
- 安装:
go get github.com/jinzhu/copier - 必须显式调用
copier.CopyWithOption(&dst, &src, copier.DeepCopy) - 不支持 interface{} 值的自动类型推导,遇到
interface{}字段需手动指定转换逻辑 - 性能比原生
json略好,但仍有反射开销,高频调用场景建议缓存copier的 struct info
自己写 Clone() 方法才是最稳的路径
标准库不提供通用深拷贝,Go 的哲学是“明确优于隐式”。为关键结构体实现 Clone() 方法,能完全掌控字段行为、避免意外引用、兼顾性能和可读性。
容易踩的坑:忘了递归调用子结构体的 Clone(),或对切片/映射做了浅拷贝(copy(dst, src) 只复制元素指针)。
- 切片要重新 make + copy:
dst.Slices = append([]T(nil), src.Slices...) - map 要遍历赋值:
dst.Map = make(map[K]V); for k, v := range src.Map { dst.Map[k] = v.Clone() } - 指针字段要判断是否为 nil,再决定是否 new + Clone
- 如果结构体含 sync.Mutex 等不可拷贝类型,Clone 方法里应初始化新实例,而非复制锁状态
func (s *MyStruct) Clone() *MyStruct {
if s == nil {
return nil
}
c := &MyStruct{ID: s.ID, Name: s.Name}
c.Items = make([]Item, len(s.Items))
for i := range s.Items {
c.Items[i] = s.Items[i].Clone()
}
return c
}
别碰 unsafe 和 reflect.DeepCopy(不存在)
Go 标准库根本没有 reflect.DeepCopy 函数——这是很多人搜错的关键点。网上有些文章教用 unsafe + reflect 手动分配内存并 memcpy,看似高效,实则极易崩溃:一旦结构体内存布局变化(比如加字段、改顺序)、含 GC 元信息(如 string、slice header),就会导致内存越界或 GC 误回收。
这种方案只在极少数嵌入式或内核级 Go 变种中出现,主线 Go 完全不支持,也不保证 ABI 稳定。
- 任何声称“一行
reflect深拷贝”的代码,背后都藏着未声明的约束(比如仅限 POD 类型) -
unsafe拷贝无法处理 interface{}、闭包、方法集,一用就错 - 即使当前跑通,升级 Go 版本后可能直接 segfault
Clone() 还是用 copier 就很清楚了。










