先参考之前写的文章 golang-对象深拷贝的常见方式及性能
本文仅介绍使用gob
时遇见的坑,熟悉go
的都知道,若结构体以小写字母
开头,则表示该字段不希望外部读到,相当于Java
的private
。本次遇见的坑便是在此基础上产生的
使用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
}
发现其字段均不对外开放,因此导致 gob
在Encode
时失败了,具体代码在 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
这种方式。