直接赋值和copy()不是深拷贝,因只复制表层数据、不递归处理引用类型;含切片、map、指针等字段时会共享底层内存,导致修改一方影响另一方。

直接赋值和copy()为什么不是深拷贝
因为它们只复制表层数据,不递归处理引用类型。结构体里只要含 []T、map[K]V、*T、chan T 或 interface{},赋值后两边就共享底层内存。
常见错误现象:src.Map["k"] = "v" 后,dst.Map["k"] 也变了;append(src.Slice, x) 导致 dst.Slice 长度意外增长。
-
copy(dst, src)只对切片有效,传结构体会编译失败 -
*dst = *src是解引用再赋值,仍是浅拷贝——嵌套指针、map 不会被展开 - 所有字段都是基本类型(
int、string、无嵌套)时,直接赋值才安全
用 json.Marshal + json.Unmarshal 快速应急
适合 DTO、配置结构体等纯数据对象,不引入第三方依赖,但本质是序列化/反序列化,不是内存级深拷贝。
示例函数:
立即学习“go语言免费学习笔记(深入)”;
func DeepCopyByJSON(src interface{}) (interface{}, error) {
data, err := json.Marshal(src)
if err != nil {
return nil, err
}
var dst interface{}
err = json.Unmarshal(data, &dst)
return dst, err
}
- 字段必须全部导出(首字母大写),否则被忽略
- 不支持
func、chan、unsafe.Pointer,遇到会报json: unsupported type -
time.Time被转成字符串,float64可能有精度损失,nil slice/map变成空而非 nil
生产环境推荐 ulule/deepcopier
编译期生成代码,零反射开销,类型安全,支持字段映射、忽略、重命名,能处理嵌套结构体和指针。
基础用法:deecopier.Copy(&src).To(&dst),两个参数都必须是指针。
- 源结构体字段必须导出,目标结构体字段必须可写(不能是未导出字段)
- 支持 tag 控制:
type DTO struct { Name string `deepcopier:"field:FullName"` } - 不支持未导出字段、
func、channel、unsafe.Pointer——这是 Go 语言限制,不是库的问题
手动写 DeepCopy() 方法最可控
当你结构固定、性能敏感、或需要自定义语义(比如跳过某些字段、特殊处理 sync.Mutex)时,手动实现是唯一可靠路径。
例如:
func (p *Person) DeepCopy() *Person {
if p == nil {
return nil
}
var ageCopy int
if p.Age != nil {
ageCopy = *p.Age
}
hobbiesCopy := make([]string, len(p.Hobbies))
copy(hobbiesCopy, p.Hobbies)
return &Person{
Name: p.Name,
Age: &ageCopy,
Hobbies: hobbiesCopy,
}
}
- 控制力强、无额外依赖、性能最高
- 容易遗漏字段,尤其加新字段后忘记更新
DeepCopy() - 无法自动穿透嵌套结构体,每层都要显式处理
真正难的不是怎么拷,而是判断“哪些字段该深拷、哪些该共享、哪些该忽略”——这得结合业务语义,没有通用答案。


















