関連記事
GitHub - devlights/blog-summary: ブログ「いろいろ備忘録日記」のまとめ
概要
以下、自分用のメモです。たまにやりたいときに忘れているので、ここにメモメモ。。。
他の言語でも同様ですが、たまに値をディープコピーしたいときがあります。そのようなとき、よくやる手段がシリアライズしてデシリアライズしてしまうというやり方です。
Goにも他の言語と同様にシリアライズ手段がいくつか用意されていますが、私はよく encoding/gob を利用します。
ただ、encoding/gobさんでも以下の型は対応出来ません。
- 関数型 (func)
- チャネル型 (chan)
- エクスポートされていないフィールド
- nilポインタ
上記のパターンの場合は、定義されていても無視されます。
gobの場合、最初の出力時に型定義も含めて出力するので、毎回エンコーダとデコーダを生成しているのは実際無駄なオーバーヘッドになるのですが、何回も同じ型をディープコピーしないといけない場合が無い限り、大抵これで済ませています。
やり方としては、以下のような関数を定義するだけです。
clone := func(from, to any) { var ( buf = new(bytes.Buffer) enc = gob.NewEncoder(buf) dec = gob.NewDecoder(buf) ) _ = enc.Encode(from) _ = dec.Decode(to) }
encoding/json の場合は、以下のようになりますね。
clone := func(from, to any) {
b, _ := json.Marshal(from)
_ = json.Unmarshal(b, to)
}
サンプル
gob_deepcopy.go
package deepcopy import ( "bytes" "encoding/gob" "github.com/devlights/gomy/output" ) // GobDeepCopy -- encoding/gob を利用した deep-copy のサンプルです. // // REFERENCES: // - https://stackoverflow.com/questions/46790190/quicker-way-to-deepcopy-objects-in-golang // - https://stackoverflow.com/questions/37618399/efficient-go-serialization-of-struct-to-disk/37620399#37620399 // - https://www.reddit.com/r/golang/comments/2vzmp7/deepcopy_of_a_slice_of_structs/ // - https://groups.google.com/forum/#!topic/golang-nuts/vK6P0dmQI84 func GobDeepCopy() error { // -------------------------------------------------------------------------- // encoding/gob パッケージを利用した deep-copy // // 他の言語でもよくやる方法であるが、手っ取り早くオブジェクトを deep-copy する方法として // 対象のオブジェクトを シリアライズ/デシリアライズ して取得する方法がある。 // // golang にて シリアライズ/デシリアライズ するメジャーな方法が以下のもの // - encoding/gob 使う // - encoding/json 使う // // 以下は encoding/gob を利用した方法 // // gobは、Go言語で表現される型のほどんどに対応しているが、以下の型は対応していない。 // - 関数型 (func) // - チャネル型(chan) // - エクスポートされていないフィールド // 上記の型の場合、定義されていても無視される。 // // また、gobは最初の出力時に型の情報を出力するようになっているため // 単発でディープコピーをする場合には encoding/json の方が速い。(jsonにはこのフェーズが無いため) // -------------------------------------------------------------------------- clone := func(from, to any) { var ( buf = new(bytes.Buffer) enc = gob.NewEncoder(buf) dec = gob.NewDecoder(buf) ) _ = enc.Encode(from) _ = dec.Decode(to) } // -------------------------------------------------------------------------- // 基本の型 var ( i = 100 i2 int s = "helloworld" s2 string ) clone(&i, &i2) clone(&s, &s2) output.Stdoutl("[i, i2]", i, i2) output.Stdoutl("[s, s2]", s, s2) // -------------------------------------------------------------------------- // スライス var ( sli1 = []int{1, 2, 3} sli2 []int ) clone(&sli1, &sli2) output.Stdoutl("[sli1, sli2][1]", sli1, sli2) sli1[0] = 100 sli2[1] = 200 output.Stdoutl("[sli1, sli2][2]", sli1, sli2) // -------------------------------------------------------------------------- // マップ var ( map1 = map[int]string{1: "apple", 2: "ringo"} map2 map[int]string ) clone(&map1, &map2) output.Stdoutl("[map1, map2][1]", map1, map2) map1[1] = "melon" map2[2] = "林檎" output.Stdoutl("[map1, map2][2]", map1, map2) // -------------------------------------------------------------------------- // 構造体 type ( A struct { Value string } B struct { A ValueB string } ) var ( b1 = B{ A: A{Value: "hello"}, ValueB: "world", } b2 B ) clone(&b1, &b2) output.Stdoutl("[b1, b2][1]", b1, b2) b1.Value = "world" b1.ValueB = "hello" output.Stdoutl("[b1, b2][2]", b1, b2) return nil }
実行
$ task task: [build] go build -o "/workspace/try-golang/try-golang" . task: [run] ./try-golang -onetime ENTER EXAMPLE NAME: deepcopy_gob [Name] "deepcopy_gob" [i, i2] 100 100 [s, s2] helloworld helloworld [sli1, sli2][1] [1 2 3] [1 2 3] [sli1, sli2][2] [100 2 3] [1 200 3] [map1, map2][1] map[1:apple 2:ringo] map[1:apple 2:ringo] [map1, map2][2] map[1:melon 2:ringo] map[1:apple 2:林檎] [b1, b2][1] {{hello} world} {{hello} world} [b1, b2][2] {{world} hello} {{hello} world} [Elapsed] 349.39µs
参考情報
Goのおすすめ書籍
過去の記事については、以下のページからご参照下さい。
サンプルコードは、以下の場所で公開しています。