项目中用了protobuf 做 RPC 协议,然后据说 gob 的encoder 和 decoder 比 protobuf 效率更高,于是写了一个用于 protobuf 结构体的深度拷贝库函数
func DeepCopy(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)
}
然而实际使用的时候发现有点坑。
gob 为了提高效率,对于 0 值是不拷贝的,不仅仅是整型的零值,还包括指向零值的指针,也不会拷贝。
测试代码如下:
package main
import (
"bytes"
"encoding/gob"
"fmt"
"log"
)
type P struct {
X int
Y *int
Z *int
Name string
}
func main() {
var network bytes.Buffer // Stand-in for a network connection
enc := gob.NewEncoder(&network) // Will write to network.
dec := gob.NewDecoder(&network) // Will read from network.
z := 0
y := 1
err := enc.Encode(P{0, &y, &z, "指向 0 值的指针的字段被忽略了"})
if err != nil {
log.Fatal("encode error:", err)
}
// Decode (receive) the value.
var p2 P
err = dec.Decode(&p2)
if err != nil {
log.Fatal("decode error:", err)
}
fmt.Printf("%q: {x:%d, y:%p, z:%p}\n", p2.Name, p2.X, p2.Y,p2.Z)
}
以上代码可以到 https://play.golang.org/p/xEB1ENZdfDl 这里去运行。
可以看到即使结构体 P 的成员 Z 不为 nil , 仅仅是 Z 指向的值为 0 , 这个指针值也不会被拷贝。而指向值不为 0 的 Y 则可以成功拷贝。
当然,如果不是用 protobuf 做协议,这个用法没有问题,可以将 Z== nil 和 *Z = 0 等效
但是对于 protobuf 来说,指针为 nil 这个值是默认不会序列化的,对于不同端(比如 iOS 或 Android 或 C ),判断可能不一样。所以protobuf 的深拷贝建议还是用 proto.Clone()
gob 相关参考: https://blog.golang.org/gobs-of-data