3

How do you copy the Item struct and all pointers to a new struct?

type Item struct {
    A    []*ASet     `json:"a,omitempty"`
    B    []*BSet.    `json:"b,omitempty"`
    C    []*CSet.    `json:"c,omitempty"`
}


type ASet struct {
    UID   string     `json:"uid,omitempty"`
    Items []*ItemA `json:"member,omitempty"`
}

type ItemA struct {
    UID  string `json:"uid,omitempty"`
    Portset []*PortSet `json:"portset,omitempty"`
}

type PortSet struct {
    UID   string     `json:"uid,omitempty"`
    Ports []*Port `json:"member,omitempty"`
}

type Port struct {
    UID  string `json:"uid,omitempty"`
    Port int    `json:"port,omitempty"`
}

I don't want the new struct to reference the old struct.

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
mbudge
  • 557
  • 13
  • 28
  • Possible duplicate of https://stackoverflow.com/questions/26598524/how-do-you-perform-a-deep-copy-of-a-struct-in-go?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa – Zzyzx May 10 '18 at 09:16

1 Answers1

8

What you want is essentially a deep copy which is not supported by the standard library.

Your choices:

  • Do the copy "manually", e.g. create a new struct and copy the fields, where pointers or slices/maps/channels/etc must be duplicated manually, in a recursive manner.
    This is easiest done by assigning your struct to another one which copies all fields, so you essentially only need to nurture pointers/maps/slices etc. (but recursively).
  • Use an external library, e.g. github.com/mohae/deepcopy, github.com/ulule/deepcopier or github.com/mitchellh/copystructure
  • Marshal your struct to some format (e.g. JSON), then unmarshal into another variable.

The last option could look like this:

var i1 Item
data, err := json.Marshal(i1)
if err != nil {
    panic(err)
}

var i2 Item
if err := json.Unmarshal(data, &i2); err != nil {
    panic(err)
}
// i2 holds a deep copy of i1

Note that marshaling/unmarshaling isn't particularly efficient, but easy and compact. Also note that this might not handle recursive data structures well, might even hang or panic (e.g. a field points to the containing struct), but handling recursive structures may be a problem to all solutions. Also note that this won't clone unexported fields.

The good thing about this marshaling / unmarshaling is that you can easily create a helper function to deep-copy "any" values:

func deepCopy(v interface{}) (interface{}, error) {
    data, err := json.Marshal(v)
    if err != nil {
        return nil, err
    }

    vptr := reflect.New(reflect.TypeOf(v))
    err = json.Unmarshal(data, vptr.Interface())
    if err != nil {
        return nil, err
    }
    return vptr.Elem().Interface(), err
}

Testing it:

p1 := image.Point{X: 1, Y: 2}
fmt.Printf("p1 %T %+v\n", p1, p1)

p2, err := deepCopy(p1)
if err != nil {
    panic(err)
}

p1.X = 11
fmt.Printf("p1 %T %+v\n", p1, p1)
fmt.Printf("p2 %T %+v\n", p2, p2)

Output (try it on the Go Playground):

p1 image.Point (1,2)
p1 image.Point (11,2)
p2 image.Point (1,2)
icza
  • 389,944
  • 63
  • 907
  • 827