Salim Fadhley
Salim Fadhley

Reputation: 8235

How do I copy a struct

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.

Upvotes: 86

Views: 174405

Answers (6)

Michael Kleinhenz
Michael Kleinhenz

Reputation: 473

The easiest way of doing this is using a library (I'd recommend https://github.com/jinzhu/copier) combined with some reflection magic:

var cloneObj MyCustomStruct = reflect.New(reflect.ValueOf(sourceObj).Elem().Type()).Interface()
copier.Copy(cloneObj, sourceObj)

This gives you a deep copy of the source struct correctly typed (which is important). If you don't use an interface, you may need to customize the reflection use a bit (remove the .Interface()).

Upvotes: 0

Plasmatium
Plasmatium

Reputation: 634

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

Upvotes: 4

Zombo
Zombo

Reputation: 1

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")
}

Upvotes: 10

Bogdan Costea
Bogdan Costea

Reputation: 69

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)
}

Upvotes: 3

Sezal Agrawal
Sezal Agrawal

Reputation: 151

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.

Upvotes: 12

maerics
maerics

Reputation: 156552

In go, primitive types, and structs containing only primitive types, are copied by value, so you can copy them by simply assigning to a new variable (or returning from a function). For example:

type Person struct{
  Name string
  Age  int
}

alice1 := Person{"Alice", 30}
alice2 := alice1
fmt.Println(alice1 == alice2)   // => true, they have the same field values
fmt.Println(&alice1 == &alice2) // => false, they have different addresses

alice2.Age += 10
fmt.Println(alice1 == alice2)   // => false, now they have different field values

Note that, as mentioned by commenters, the confusion in your example is likely due to the semantics of the test library you are using.

If your struct happens to include arrays, slices, or pointers, then you'll need to perform a deep copy of the referenced objects unless you want to retain references between copies. Golang provides no builtin deep copy functionality so you'll have to implement your own or use one of the many freely available libraries that provide it.

Upvotes: 183

Related Questions