'How do I copy a struct in Golang?
I want to copy an object so that I have two identical objects with two different memory addresses. My first attempt at this has failed:
aa := a
assert.NotEqual(t, &a, &aa, "Copied items should not be the same object.") // Test fails
Can I fix this so that it really does a copy of the struct? There's nothing special about this structure.
Solution 1:[1]
DeepCopy is a very heavy operation and hence should be avoided if possible. For complex structures such as the following, we can optimise the code.
type Address struct {
city string
state string
}
type Person struct {
age int
name string
address []Address
}
p := Person{
age: 20,
name: "Jack",
address: []Address{
{
city: "city1",
state: "state1",
}, {
city: "city2",
state: "state2",
},
},
}
var q Person
q.age = p.age
q.name = p.name
q.address = append(q.address, p.address...)
q.address[0].city = "cityx"
Result:
p object:
{20 Jack [{city1 state1} {city2 state2}]}
q object:
{20 Jack [{cityx state1} {city2 state2}]}
Inference:
As can be seen from the above example, the p object did not change when q was changed. This approach can be used in nested array of structs.
Solution 2:[2]
You can use a function with pass by value and return the argument untouched or changed depending on your needs.
Using the structs from above:
func main() {
copyOf := func(y Person) Person {
y.name = "Polonius"
y.address = append(y.address, Address{
city: "other city",
state: "other state",
})
return y
}
p := Person{
age: 20,
name: "Jack",
address: []Address{
{
city: "city1",
state: "state1",
}, {
city: "city2",
state: "state2",
},
},
}
q := copyOf(p)
fmt.Printf("Orig %v, \naddrss: %p \n\n", p, &p)
fmt.Printf("Copy %v, \naddress: %p\n\n", q, &q)
}
Solution 3:[3]
Be careful, if your source struct is actually a pointer, then a normal assign won't work:
package main
import "net/http"
func main() {
a, err := http.NewRequest("GET", "https://stackoverflow.com", nil)
if err != nil {
panic(err)
}
b := a.URL
b.Host = "superuser.com"
println(a.URL.Host == "superuser.com")
}
Instead, you need to dereference the pointer:
package main
import "net/http"
func main() {
a, err := http.NewRequest("GET", "https://stackoverflow.com", nil)
if err != nil {
panic(err)
}
b := *a.URL
b.Host = "superuser.com"
println(a.URL.Host == "stackoverflow.com")
}
Solution 4:[4]
You could try gob.Encode then gob.Decode, like we do in javascript: first JSON.stringify then JSON.parse.
In golang you should use gob instead of json.Marshal/json.Unmarshal because you don't need to add json tags on fields, and you can handle interface by using gob. Every interface used in you struct field should be called by gob.Register
import (
"bytes"
"encoding/gob"
)
func DeepCopy(src, dist interface{}) (err error){
buf := bytes.Buffer{}
if err = gob.NewEncoder(&buf).Encode(src); err != nil {
return
}
return gob.NewDecoder(&buf).Decode(dist)
}
dist should always be a pointer
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Attilio |
| Solution 2 | Bogdan Costea |
| Solution 3 | Zoe stands with Ukraine |
| Solution 4 | Plasmatium |
