Golangにおけるディープコピー:技術とベストプラクティス
James Reed
Infrastructure Engineer · Leapcell

Key Takeaways
- Goにおけるディープコピーは、共有データへの意図しない変更を避けるために不可欠です。
- スライス、マップ、および参照型を持つ構造体は、明示的なディープコピーが必要です。
copier
のようなサードパーティライブラリは、ディープコピー操作を簡素化できます。
ソフトウェア開発では、データ構造のコピーを作成することが一般的な要件です。Go(Golang)では、ディープコピーをどのように実行するかを理解することが、あるデータ構造への変更が別のデータ構造に不注意に影響を与えないようにするために不可欠です。この記事では、Goでディープコピーを実現するためのさまざまな方法を探ります。
シャローコピーとディープコピーの理解
ディープコピーの手法に入る前に、シャローコピーとディープコピーの違いを区別することが重要です。
-
シャローコピー: 元のデータと同じ基盤となるデータを参照する新しい変数を作成します。データへの変更は、オリジナルとコピーの両方に影響を与えます。
-
ディープコピー: データ独自の独立したコピーを持つ新しい変数を作成します。コピー内のデータへの変更は、オリジナルには影響しません。
プリミティブ型のディープコピー
Goのプリミティブ型(整数、浮動小数点数、ブール値、文字列など)は、値によってコピーされます。ある変数を別の変数に代入すると、ディープコピーが作成されます。
a := 42 b := a b = 100 // 'a' は 42 のまま、'b' は 100
この例では、a
と b
は独立しています。b
を変更しても a
には影響しません。
複合型のディープコピー
配列、スライス、マップ、構造体などの複合型の場合、ディープコピーにはより注意が必要です。
配列
配列はGoでは値型です。ある配列を別の配列に代入すると、ディープコピーになります。
arr1 := [3]int{1, 2, 3} arr2 := arr1 arr2[0] = 10 // 'arr1' は [1, 2, 3] のまま、'arr2' は [10, 2, 3]
スライス
スライスは参照型です。単純な代入は、スライスヘッダーをコピーしますが、基盤となるデータはコピーしません。
slice1 := []int{1, 2, 3} slice2 := slice1 slice2[0] = 10 // 'slice1' と 'slice2' の両方が [10, 2, 3]
スライスをディープコピーするには、基盤となるデータをコピーする必要があります。
slice1 := []int{1, 2, 3} slice2 := make([]int, len(slice1)) copy(slice2, slice1) slice2[0] = 10 // 'slice1' は [1, 2, 3]、'slice2' は [10, 2, 3]
マップ
マップも参照型です。直接代入すると、同じ基盤となるデータが共有されます。
map1 := map[string]int{"a": 1, "b": 2} map2 := map1 map2["a"] = 10 // 'map1' と 'map2' の両方が {"a": 10, "b": 2}
マップをディープコピーするには:
map1 := map[string]int{"a": 1, "b": 2} map2 := make(map[string]int) for k, v := range map1 { map2[k] = v } map2["a"] = 10 // 'map1' は {"a": 1, "b": 2}、'map2' は {"a": 10, "b": 2}
構造体
構造体は値型です。ある構造体を別の構造体に代入すると、データがコピーされます。
type Point struct { X, Y int } p1 := Point{1, 2} p2 := p1 p2.X = 10 // 'p1' は {1, 2}、'p2' は {10, 2}
ただし、構造体に参照型(スライス、マップ、ポインタなど)が含まれている場合は、それらのフィールドを手動でディープコピーする必要があります。
サードパーティライブラリの使用
複雑なデータ構造の場合、ディープコピーロジックを手動で実装すると、エラーが発生しやすくなります。copierのようなサードパーティライブラリは、プロセスを簡素化できます。
import "github.com/jinzhu/copier" type Person struct { Name string Age int Friends []string } p1 := Person{Name: "Alice", Age: 30, Friends: []string{"Bob", "Charlie"}} var p2 Person copier.Copy(&p2, &p1) p2.Friends[0] = "David" // 'p1.Friends' は ["Bob", "Charlie"]、'p2.Friends' は ["David", "Charlie"]
特にネストされた型や複雑な型の場合、ライブラリがどのようにディープコピーを実行するかを理解していることを確認してください。
結論
Goでのディープコピーは、関連するデータ型を慎重に検討する必要があります。プリミティブ型は簡単ですが、複合型は真のディープコピーを保証するために明示的な処理が必要です。サードパーティライブラリを利用すると、複雑な構造の管理に役立ちますが、常にそれらの動作がアプリケーションの要件と一致していることを確認してください。
FAQs
スライスとマップは参照型であり、代入は基盤となるデータではなく、参照のみをコピーすることを意味します。
新しいスライスを手動で割り当て、各要素をコピーして、真のディープコピーを保証します。
手動でのディープコピーがエラーが発生しやすい複雑なネストされた構造を扱う場合。
Goプロジェクトのホスティングには、Leapcellをお選びください。
Leapcellは、Webホスティング、非同期タスク、Redisのための次世代サーバーレスプラットフォームです。
多言語サポート
- Node.js、Python、Go、またはRustで開発。
無制限のプロジェクトを無料でデプロイ
- 使用量に応じてのみ支払い - リクエストも料金もなし。
比類なきコスト効率
- アイドル料金なしの従量課金。
- 例:25ドルで、平均応答時間60msで694万リクエストをサポート。
合理化された開発者エクスペリエンス
- 簡単なセットアップのための直感的なUI。
- 完全に自動化されたCI/CDパイプラインとGitOps統合。
- 実行可能な洞察のためのリアルタイムのメトリクスとロギング。
簡単なスケーラビリティと高性能
- 高い並行処理を簡単に処理するための自動スケーリング。
- 運用上のオーバーヘッドはゼロ - 構築に集中するだけです。
ドキュメントで詳細をご覧ください!
Xでフォローしてください:@LeapcellHQ