Go语言无深拷贝特性,赋值均为值传递;struct浅拷贝逐字段复制:值类型完全独立,引用类型共享底层数据;slice需make+copy实现真隔离;反射或序列化深拷贝有局限,手动按语义逐字段处理最可靠。
Go 语言里没有“深拷贝”这个语言特性,所有赋值、传参、返回都是值传递;所谓深/浅,只取决于你操作的是什么类型、它底层是否含引用字段。
写 copy := original 时,Go 会逐字段复制:
int、string、不含指针的嵌套 struct):内存内容完整复制,改 copy.Age 不影响 original.Age
[]T、map[K]V、*T、chan T):只复制头信息(如 slice 的 len/cap/底层数组指针),底层数据仍共用例如 original.Tags 和 copy.Tags 指向同一底层数组,改 copy.Tags[0] 就等于改 original.Tags[0];original.Info 和 copy.Info 若为 *Detail,则两者解引用后操作的是同一块内存。
copy() 才算值拷贝b := a 或 b := a[:] 都是浅拷贝——两个 slice 共享底层数组。要真正隔离,得手动分配新底层数组:
立即学习“go语言免费学习笔记(深入)”;
make([]T, len(a)) 分配空间copy(b, a) 复制元素值T 本身含引用字段(如 []map[string]int),copy() 只复制外层 slice,内部 map 仍共享,需额外递归处理不这么做,哪怕只改 b[0].Name,a[0].Name 也会变。
第三方库如 deepcopy 或手写反射递归,能自动处理嵌套 map、slice、struct,但有硬伤:
func、chan、unsafe.Pointer 无法安全复制,通常 panic 或忽略json.Marshal + json.Unmarshal)会丢掉方法、空接口类型、循环引用,且要求字段可导出这些不是“实现不到位”,而是 Go 类型系统本身不支持通用深拷贝——你得自己决定哪些字段该深、哪些该共享、哪些该忽略。
比起依赖通用方案,更可靠的是按业务语义逐字段写拷贝逻辑:
[]T:用 make + copy,若 T 是结构体且含指针,对每个 T 再调用其自有拷贝方法map[K]V:make(map[K]V) 后遍历赋值,若 V 是引用类型,也需递归深拷贝*T:判空后 dst = &(*src),若 T 有引用字段,继续展开最易被忽略的点是:深拷贝不是技术问题,而是设计问题——你要拷什么、不拷什么、怎么处理 nil、如何应对自引用,这些都没法靠一个函数自动推断。