Goにおけるmapおよびslice構造体は、コピーとメモリ使用の重要な特性を持ち、経験の浅い開発者に予期しない挙動を引き起こすことがあります。
Goは静的型付けの厳しい言語と見なされ、デフォルトではポインタが存在しませんが、mapとsliceには特別な内部モデルが実装されています。この2つのタイプは参照型構造体です。これにより、これらのオブジェクトをコピーし転送する際に制限があり、複雑なニュアンスを生じます。
mapとsliceのコピーは、その内容を深くコピーするのではなく、同じオブジェクトへの新しい参照を形成します。これにより、データの変更、関数からの値の不正な返却、変更の際に予期しない副作用が生じます。加えて、mapまたはsliceを関数の結果として返すことは、追加のアロケーションやメモリリークを引き起こす可能性があります。
copy()を使用する必要があります。正しいコピーの例:
// スライスのコピー a := []int{1, 2, 3} b := make([]int, len(a)) copy(b, a) // bは今aから独立 // mapのコピー src := map[string]int{"x": 1} dst := make(map[string]int) for k, v := range src { dst[k] = v }
主な特性:
sliceとmapは参照型であり、内容ではなく記述子によってコピーされる一方のmap/sliceを他方に単に代入し、そして一方を変更した場合はどうなるか?
mapとsliceは同じメモリ内のデータを指し示し、変更が両方のオブジェクトに影響を及ぼします。
関数からsliceまたはmapを返すときに「メモリ効率が良い」とよく言われるのはなぜか?
それは、全内容ではなく記述子のコピーが返され、ヒープ内のデータは参照がある限り生存するからです。
copy()関数を使用してmapの「深い」コピーを作成できるか?
できません。copy()はスライスと配列にのみ機能し、mapには常にループが必要です。
開発者がsliceまたはmapを代入でコピーし、その安全のためにコピーを変更する:
利点:
欠点:
必要なデータを変更する前にsliceの場合はcopy()、mapの場合はループを使用:
利点:
欠点: