golang对象深拷贝之使用gob的坑

先参考之前写的文章 golang-对象深拷贝的常见方式及性能

本文仅介绍使用gob时遇见的坑,熟悉go的都知道,若结构体以小写字母开头,则表示该字段不希望外部读到,相当于Javaprivate。本次遇见的坑便是在此基础上产生的

使用gob针对对象进行深拷贝,代码如下

go 复制代码
func copy(dst, src interface{}) error {
   var buf bytes.Buffer
   if err := gob.NewEncoder(&buf).Encode(src); err != nil {
      return err
   }
   return gob.NewDecoder(bytes.NewBuffer(buf.Bytes())).Decode(dst)
}

正常的case

go 复制代码
func TestCopy(t *testing.T) {
   type NormalData struct {
      Name   string
      Gender int32 //0:woman 1:man 2:other
   }

   normalDataSrc := &NormalData{
      Name:   "This is whoops",
      Gender: int32(1),
   }

   normalDataDst := &NormalData{}
   copy(normalDataDst, normalDataSrc)
   normalDataDst.Name = "this is a clone"

   fmt.Println(normalDataSrc, normalDataDst)
}

上述代码输出以下内容:

shell 复制代码
&{This is whoops 1} &{this is a clone 1}

可以发现copy无问题。

错误的case

golang 复制代码
func TestErrorCopy(t *testing.T) {
   type NormalData struct {
      Name   string
      Gender int32 //0:woman 1:man 2:other
      Lock   *sync.Mutex
   }

   normalDataSrc := &NormalData{
      Name:   "This is whoops",
      Gender: int32(1),
      Lock:   &sync.Mutex{},
   }

   normalDataDst := &NormalData{}
   if err := copy(normalDataDst, normalDataSrc); err != nil {
      fmt.Printf("copy err %s\n", err.Error())
      return
   } else {
      normalDataDst.Name = "this is a clone"
      fmt.Println(normalDataSrc, normalDataDst)
   }

}

上述代码输出以下内容:

shell 复制代码
copy err gob: type sync.Mutex has no exported fields

考虑下这是为什么呢?明明Lock是大写的是可以被导出的,为什么报错了?

根据错误内容分析下,可以发现主要发生在 sync.Mutex 这个类型,查看其源码

go 复制代码
type Mutex struct {
   state int32
   sema  uint32
}

发现其字段均不对外开放,因此导致 gobEncode时失败了,具体代码在 gob/encode.go[go1.16]647 行,可以自行debug进行调试。

那么怎么解决这种错误呢?参考下述代码

gob深拷贝结合自定义copy

实现起来就是field全为大写的可以通过gob这种深拷贝,存在field为小写的采用自定义复制方式。

go 复制代码
func TestNoExportedCopy(t *testing.T) {
   type OuterData struct {
      Name   string
      Gender int32 //0:woman 1:man 2:other
   }

   type InnerData struct {
      Lock *sync.Mutex
   }
   type NormalData struct {
      //可copy部分,即无小写字母开头的field
      *OuterData
      //不可copy部分,即存在小写字母开头的field
      *InnerData
   }

   normalDataSrc := &NormalData{
      OuterData: &OuterData{
         Name:   "This is whoops",
         Gender: int32(1)},
      InnerData: &InnerData{
         Lock: &sync.Mutex{},
      },
   }

   normalOuterDataDst := &OuterData{}
   if err := copy(normalOuterDataDst, normalDataSrc.OuterData); err != nil {
      fmt.Printf("copy err %s\n", err.Error())
      return
   } else {
      normalDataDst := &NormalData{OuterData: normalOuterDataDst}
      normalDataDst.OuterData.Name = "this is a clone"
      normalDataDst.InnerData = normalDataSrc.InnerData
      fmt.Println("原结构体:", normalDataSrc.OuterData, &normalDataSrc.Lock)
      fmt.Println("copy结构体:", normalDataDst.OuterData, &normalDataDst.Lock)
   }
}

上述代码输出以下内容:

shell 复制代码
原结构体: &{This is whoops 1} 0xc000010a80
copy结构体: &{this is a clone 1} 0xc000010a80

综上需要注意在使用gob时虽然代码相对简洁,但是若结构体存在小写的field,则会导致 type xxxxx has no exported fields。若想解决这点,在必须存在小写filed时,需考虑将字段通过xxxOuter,xxxInner的方式将结构体进行拆分,并结合自定义copy方式来实现。

注: 个人还是比较建议优先多动手去一一赋值,其次使用json序列化与反序列化方式,最后再采用gob这种方式。

Tips: 上述 Lock字段实际上并未发生copy,只是指向了原有指针地址。

相关推荐
未来魔导5 小时前
go语言中json操作总结(下)
数据分析·go·json
未来魔导8 小时前
Go-qdrant-API开启客服系统新模式
go·api·qdrant
喵个咪14 小时前
开箱即用的 GoWind Admin|风行,企业级前后端一体中后台框架:Makefile 在后端开发中的应用与 Windows 环境配置
后端·go
未来魔导1 天前
go语言中json操作总结
数据分析·go·json
王中阳Go背后的男人1 天前
GoFrame vs Laravel:从ORM到CLI工具的全面对比与迁移指南
后端·go
卡尔特斯1 天前
Go 安装插件更换国内镜像
go
王中阳Go1 天前
都2026年了,PHP还纠结转Go还是Java呢?安利一个无缝迁移的框架~
java·后端·go
卡尔特斯1 天前
go get 快速入门(自用笔记)
go
得物技术1 天前
Go语言在高并发高可用系统中的实践与解决方案|得物技术
go
喵个咪2 天前
开箱即用的 GoWind Admin|风行,企业级前后端一体中后台框架:深度解析 Wire 依赖注入集成实践
后端·go