🥏
【Go】スライスやマップのコピーについて
はじめに
Slices and maps contain pointers to the underlying data so be wary of scenarios when they need to be copied.
こちらのドキュメントのCopy Slices and Maps at Boundaries
に記載されているように、Goでスライスやマップをコピーするときには、注意が必要です。
ドキュメントにはGoodとBadのサンプルが記載されていますので、それぞれどのような挙動になるか、コードを動かして確認します。
挙動を確認する
引数として渡した元データの変更が、コピー先に影響してしまう例
ドキュメントのBadの例を参考に、実行可能なコードを用意しました。
package main
import "fmt"
type Trip struct {
Name string
}
type Driver struct {
trips []Trip
}
func (d *Driver) SetTrips(trips []Trip) {
d.trips = trips
}
func main() {
trips := []Trip{
{Name: "A"},
{Name: "B"},
{Name: "C"},
}
d1 := &Driver{}
d1.SetTrips(trips) // (1)
fmt.Println("Before d1: ", d1)
fmt.Println("Before trips: ", trips)
trips[0] = Trip{Name: "Changed"} // (2)
fmt.Println("After d1: ", d1)
fmt.Println("After trips: ", trips)
}
このコードを実行すると、つぎのような結果が得られます。
Before d1: &{[{A} {B} {C}]}
Before trips: [{A} {B} {C}]
After d1: &{[{Changed} {B} {C}]}
After trips: [{Changed} {B} {C}]
(1)でtrips
というスライスをd1.SetTrips()
に渡した後に、(2)でtrips
の0番目の要素を変更しています。
その後にd1
とtrips
の変数を出力していますが、trips
の0番目の要素の変更が、d1
にも影響してしまっています。
引数として渡した元データの変更が、コピー先に影響しない例
つぎにGoodの例を参考に、実行可能なコードを用意しました。
package main
import "fmt"
type Trip struct {
Name string
}
type Driver struct {
trips []Trip
}
func (d *Driver) SetTrips(trips []Trip) {
d.trips = make([]Trip, len(trips)) // (3)
copy(d.trips, trips)
}
func main() {
trips := []Trip{
{Name: "A"},
{Name: "B"},
{Name: "C"},
}
d1 := &Driver{}
d1.SetTrips(trips)
fmt.Println("Before d1: ", d1)
fmt.Println("Before trips: ", trips)
trips[0] = Trip{Name: "Changed"}
fmt.Println("After d1: ", d1)
fmt.Println("After trips: ", trips)
}
Badの例との違いは、SetTrips()
の引数のtripsをcopy()
で設定していることです(3)。
このコードを実行した結果はつぎのようになりました。
Before d1: &{[{A} {B} {C}]}
Before trips: [{A} {B} {C}]
After d1: &{[{A} {B} {C}]}
After trips: [{Changed} {B} {C}]
copy()
を利用したことで、trips
とd1.trips
が同じポインタを参照しないようになり、trips
への変更がd1
に影響しないようになりました。
Discussion