[golang] 構造体(struct)のdeepcopy

golangで構造体のdeepcopyを行う方法のメモです。

ポインタをメンバに持った構造体のdeepcopyをgolangだとどんな感じでできるのか気になったので調べてみました。

deepcopy

https://godoc.org/github.com/getlantern/deepcopy

package main
import (
  "github.com/getlantern/deepcopy"
)

type Sample struct {
  Value *string
}

func main() {
  v := "abc"
  sample := &Sample{Value: &v}
  sampleCopy := new(Sample)
  deepcopy.Copy(sampleCopy, sample)
  *sampleCopy.Value = "ABC"
  
  println(*sample.Value) // "abc"
  println(*sampleCopy.Value) // "ABC"
}

第一引数に、コピー先、第二引数にコピー元を渡してあげます。

実装

どんな実装で、deepcopyを実装しているのか気になったので中身を覗いてみました。

package deepcopy

import (
  "encoding/json"
  "fmt"
)

// Make a deep copy from src into dst.
func Copy(dst interface{}, src interface{}) error {
  if dst == nil {
    return fmt.Errorf("dst cannot be nil")
  }
  if src == nil {
    return fmt.Errorf("src cannot be nil")
  }
  bytes, err := json.Marshal(src)
  if err != nil {
    return fmt.Errorf("Unable to marshal src: %s", err)
  }
  err = json.Unmarshal(bytes, dst)
  if err != nil {
    return fmt.Errorf("Unable to unmarshal into dst: %s", err)
  }
  return nil
}

ものすごくシンプルな実装でした。

一旦json.Marshalでjsonのバイト列を作成してから、json.Unmarshalで構造体に戻しているようです。
これなら、参照値をメンバに持つ構造体でもdeepcopyされるので安心です。

だた、構造体のメンバにjson:"-"のようなアノテーションを付けていると、そのメンバはコピーされなくなってしまうので注意が必要かもしれません。