在Golang中深度复制数据结构

时间:2019-05-29 07:29:11

标签: go struct deep-copy

我想复制一个数据结构的实例。由于go没有任何内置函数,因此我正在使用第三方库:https://github.com/emirpasic/gods

例如,我可以尝试使用具有哈希集的深度复制。

var c, d hashset.Set
c = *hashset.New()
c.Add(1)
deepcopy.Copy(d, c)
c.Add(2)
fmt.Println(c.Contains(2))
fmt.Println(d.Contains(2))
fmt.Println(c.Contains(1))
fmt.Println(d.Contains(1))

但是,哈希集的内容根本不会被复制。我知道深层复制模块无法复制未导出的值,但是由于库中没有内置的“复制构造函数”,是否就意味着在不修改其代码的情况下无法与该库完全复制数据结构实例? (类似的问题发生在我研究过的其他一些库中。)

我对golang并不陌生,感觉不对劲,因为类似的事情很容易实现,例如在C ++中。我知道我可以编写自己的版本或修改其代码,但是这比预期的工作量太大,所以我认为应该采用惯用的方式。

PS:对于可能会说“不需要这种功能”的人,我正在将一些具有某些数据结构的复杂状态分配给并行计算线程,他们直接使用这些状态,并且不得互相干扰。

3 个答案:

答案 0 :(得分:1)

无论是否不幸,Go都无法做到这一点。首先想到的工具是反射(包reflect),但是使用反射,您只能读取未导出的字段,但不能进行设置。参见How to clone a structure with unexported field?

克隆具有未导出字段的结构的唯一方法是使用软件包unsafe(请参见此处的示例:Access unexported fields in golang/reflect?),但正如其名称所言:这是不安全您应该尽可能远离它。使用unsafe创建的程序不能保证它们可以继续与更高版本的Go一起使用,或者不能在每个平台上都具有相同的行为。

通常在Go中支持克隆的唯一正确方法是程序包本身是否支持此类操作。

注释#1:

这并不意味着在某些特定于 的情况下,您无法通过创建新值并手动建立其状态来“模仿”克隆。例如,您可以通过创建新地图,迭代原始地图的键值对并将其设置在新地图中来克隆map

注释2:

请注意,只需将assigning复制到另一个结构变量(相同类型),即可“精确”复制具有未导出字段的结构。

如本例所示:

type person struct {
    Name string
    age  *int
}

age := 22
p := &person{"Bob", &age}
fmt.Println(p)

p2 := new(person)
*p2 = *p
fmt.Println(p2)

将输出(在Go Playground上尝试):

&{Bob 0x414020}
&{Bob 0x414020}

我们甚至可以在不依赖具体类型的情况下使用reflect进行概括:

type person struct {
    Name string
    age  *int
}

age := 22
p := &person{"Bob", &age}
fmt.Println(p)

v := reflect.ValueOf(p).Elem()
vp2 := reflect.New(v.Type())
vp2.Elem().Set(v)
fmt.Println(vp2)

Go Playground上尝试这个。

但是我们不能做的是将person.age未导出字段更改为指向其他内容。没有声明包的帮助,它只能是nil或相同的指针值(指向对象的原始字段)。

另请参阅相关内容:Quicker way to deepcopy objects in golang

答案 1 :(得分:0)

如果您使用的软件包没有为数据结构提供复制功能,那么我将编写自己的类型安全的复制功能。此外,我不必担心未导出的字段,因为开发人员决定将这些字段隐藏给用户一定是有原因的。

在您的示例中:

func NewHashSetCopy(src *hashset.Set) *hashset.Set{
    dst := *hashset.New()
    iterator := src.Iter()
    for elem := range iterator {
        dst.Add(elem)
    }
    return dst
}

答案 2 :(得分:0)

如果您需要对 protobuf 结构进行深度复制,可以在 https://github.com/golang/protobuf 中使用 proto.Clone,请参阅 godoc 以获取更多帮助。