May*_*yoi 19 struct deep-copy go
我想复制数据结构的实例。由于 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))
Run Code Online (Sandbox Code Playgroud)
然而,哈希集的内容根本没有被复制。我知道深层复制模块无法复制未导出的值,但是由于库中没有内置的“复制构造函数”,这是否意味着无法在不修改其代码的情况下使用库完全复制数据结构实例?(我研究过的其他一些库也出现类似的问题)。
我是 golang 新手,感觉不对,因为类似的事情可以很容易地实现,例如在 C++ 中。我知道我可以编写自己的版本或修改他们的代码,但这比预期的工作量太多,这就是为什么我认为应该有一种惯用的方法。
PS:对于那些可能会说“不需要这样的功能”的人,我将一些具有某些数据结构的复杂状态分配给并行计算线程,他们直接使用状态并且不能互相干扰。
icz*_*cza 16
不幸的是,无论是否不幸,在 Go 中都没有办法做到这一点。我想到的第一个工具是反射(package reflect),但是使用反射你只能读取未导出的字段,但不能设置它们。请参阅如何克隆具有未导出字段的结构?
克隆具有未导出字段的结构的唯一方法是使用 package unsafe(请参阅此处的示例:Access unexported fields in golang/reflect?),但正如其名称所示:它是不安全的,您应该尽可能远离它。使用创建的程序unsafe不能保证它们可以继续与较新的 Go 版本一起使用,或者它们在每个平台上的行为都相同。
一般来说, Go 中支持克隆的唯一且正确的方法是包本身支持此类操作。
注意#1:
这并不意味着在某些特定情况下您不能通过创建新值并手动构建其状态来“模仿”克隆。例如,您可以map通过创建新映射、迭代原始映射的键值对并将它们设置在新映射中来克隆 a。
笔记2:
请注意,您可以通过简单地将具有未导出字段的结构体分配给另一个结构体变量(相同类型)来制作它们的“精确”副本,这也将正确复制未导出的字段。
就像这个例子一样:
type person struct {
Name string
age *int
}
age := 22
p := &person{"Bob", &age}
fmt.Println(p)
p2 := new(person)
*p2 = *p
fmt.Println(p2)
Run Code Online (Sandbox Code Playgroud)
它将输出(在Go Playground上尝试):
&{Bob 0x414020}
&{Bob 0x414020}
Run Code Online (Sandbox Code Playgroud)
我们甚至可以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)
Run Code Online (Sandbox Code Playgroud)
在Go Playground上试试这个。
但我们不能做的就是更改person.age未导出的字段以指向其他内容。如果没有声明包的帮助,它只能是nil或相同的指针值(指向与原始字段相同的对象)。
另请参阅相关内容:Quicker way to deepcopy objects in golang
如果您的结构是可序列化的,您可以将其转换为 JSON 并返回,这对于我的用例来说已经足够了。
func CloneMyStruct(orig *model.MyStruct) (*model.MyStruct, error) {
origJSON, err := json.Marshal(orig)
if err != nil {
return nil, err
}
clone := model.MyStruct{}
if err = json.Unmarshal(origJSON, &clone); err != nil {
return nil, err
}
return &clone, nil
}
Run Code Online (Sandbox Code Playgroud)