Golang中的深度复制数据结构

7vhp5slm  于 2023-11-14  发布在  Go
关注(0)|答案(5)|浏览(177)

我想复制一个数据结构的示例。由于go没有任何内置函数,我使用了第三方库:https://github.com/emirpasic/gods
例如,我可以尝试使用带有哈希集的深度复制。

  1. var c, d hashset.Set
  2. c = *hashset.New()
  3. c.Add(1)
  4. deepcopy.Copy(d, c)
  5. c.Add(2)
  6. fmt.Println(c.Contains(2))
  7. fmt.Println(d.Contains(2))
  8. fmt.Println(c.Contains(1))
  9. fmt.Println(d.Contains(1))

字符串
然而,哈希集的内容根本不会被复制。我知道深度复制模块不能复制未导出的值,但是由于库中没有内置的“复制构造函数”,这是否意味着不修改库的代码就不可能完全复制数据结构示例?(类似的问题也发生在我研究的其他一些库中)。
我对golang不熟悉,感觉不太对,因为类似的事情可以很容易地在C++中实现。我知道我可以编写自己的版本或修改他们的代码,但这比预期的要多得多,这就是为什么我认为应该有一种惯用的方法。
附言:对于那些可能会说“不需要这样的功能”的人来说,我正在将一些复杂的状态与一些数据结构分发给并行计算线程,它们直接使用状态,并且不得相互干扰。

yzuktlbb

yzuktlbb1#

不幸的是,在Go中没有办法做到这一点。首先想到的工具是反射(包reflect),但是使用反射你只能读取未导出的字段,但是你不能设置它们。参见How to clone a structure with unexported field?
克隆带有未导出字段的结构的唯一方法是使用unsafe包(参见这里的示例:在golang/reflect中访问未导出字段?),但正如其名称所言:它是 * 不安全的 *,您应该尽可能远离它。使用unsafe创建的程序不能保证它们继续在较新的Go版本中工作,或者它们在每个平台上的行为都是一样的。
一般来说,在Go语言中支持克隆的唯一和正确的方法是软件包本身支持这样的操作。

注1:

这并不意味着在某些特定情况下,你不能通过创建一个新值并手动构建其状态来“模仿”克隆。例如,你可以通过创建一个新Map来克隆一个map,迭代原始Map的键值对并在新Map中设置它们。

注2:

请注意,您可以通过简单地将其分配给另一个结构变量(相同类型)来制作具有未导出字段的结构的“精确”副本,这也将正确复制未导出的字段。
就像这个例子:

  1. type person struct {
  2. Name string
  3. age *int
  4. }
  5. age := 22
  6. p := &person{"Bob", &age}
  7. fmt.Println(p)
  8. p2 := new(person)
  9. *p2 = *p
  10. fmt.Println(p2)

字符串
这将输出(在Go Playground上尝试):

  1. &{Bob 0x414020}
  2. &{Bob 0x414020}


我们甚至可以使用reflect进行泛化,而不依赖于具体类型:

  1. type person struct {
  2. Name string
  3. age *int
  4. }
  5. age := 22
  6. p := &person{"Bob", &age}
  7. fmt.Println(p)
  8. v := reflect.ValueOf(p).Elem()
  9. vp2 := reflect.New(v.Type())
  10. vp2.Elem().Set(v)
  11. fmt.Println(vp2)


Go Playground上试试这个。

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

标签:Quicker way to deepcopy objects in golang

展开查看全部
lvmkulzt

lvmkulzt2#

如果你的结构是可序列化的,你可以把它转换成JSON,然后再转换回来,这对我的用例来说已经足够了。

  1. func CloneMyStruct(orig *model.MyStruct) (*model.MyStruct, error) {
  2. origJSON, err := json.Marshal(orig)
  3. if err != nil {
  4. return nil, err
  5. }
  6. clone := model.MyStruct{}
  7. if err = json.Unmarshal(origJSON, &clone); err != nil {
  8. return nil, err
  9. }
  10. return &clone, nil
  11. }

字符串

ghg1uchk

ghg1uchk3#

如果你需要深度复制一个protobuf结构体,你可以在https://github.com/golang/protobuf中使用proto.Clone,请参阅godoc以获得更多帮助。

5m1hhzi4

5m1hhzi44#

如果你正在使用的包没有提供数据结构的复制函数,那么我会写我自己的类型安全的复制函数。此外,我不会担心未导出的字段,因为开发人员决定向用户隐藏这些字段一定有原因。
在您的示例中:

  1. func NewHashSetCopy(src *hashset.Set) *hashset.Set{
  2. dst := *hashset.New()
  3. iterator := src.Iter()
  4. for elem := range iterator {
  5. dst.Add(elem)
  6. }
  7. return dst
  8. }

字符串

juud5qan

juud5qan5#

Reprint library支持深度复制,包括复制未导出的字段。

  1. package main
  2. import (
  3. "fmt"
  4. "github.com/qdm12/reprint"
  5. )
  6. type Test struct {
  7. field []int
  8. }
  9. func main() {
  10. orig := Test{field: []int{1, 2}}
  11. copy := reprint.This(orig).(Test)
  12. orig.field[0] = 3
  13. fmt.Println("orig", orig.field[0])
  14. fmt.Println("copy", copy.field[0])
  15. }

字符串

展开查看全部

相关问题