とりあえずサンプル
スライスsを、スライスtにコピーしたいとする。
package main
import "fmt"
func main() {
s := []int{0, 10, 20, 30} // => [0 10 20 30] ←スライスsを作成
t := make([]int, len(s)) // スライスtの骨組みを作成する
fmt.Println(t) // => [0 0 0 0] ←まだ値には0が入っている
n := copy(t, s) // スライスsをスライスtにコピーする
fmt.Println(t) // => [0 10 20 30] ←値がスライスsと同じ値になった
fmt.Println(n) // => 4 ←コピーされた要素の個数が返されている
}
どうして、t := s
としないのか
下記のように「copy関数」を使わずに代入するのはダメ?
package main
import "fmt"
func main() {
s := []int{0, 10, 20, 30}
t := s
fmt.Println(s) // => [0, 10, 20, 30]
fmt.Println(t) // => [0, 10, 20, 30] ←コピーできてるくない?
}
上のコードのように、シンプルに代入したら同じスライスができているのに、これではダメなのか…?
結論から言うと、これダメです。
罠
上のコードに、もう少し追記すれば分かるかと思います。
package main
import "fmt"
func main() {
s := []int{0, 10, 20, 30}
t := s
t[0] = 100 // 1番目の値を0から100に書き換える
fmt.Println(t) // [100 10 20 30] ←1番目の値は書き換えられているので100
fmt.Println(s) // [100 10 20 30] ←あれ? スライスsは書き換えていないのに…?
}
このように、コピー先のスライスを編集すると、コピー元のスライスにまで反映されてしまいます。
これは、Goのスライスが「参照型」なためとのことです。
Goでスライスを使う場合には、慣れるまでは、この参照型によってバグが発生するといったことも出てきそうです。。
2019/08/13 追記)
コメントでご指摘頂いており、**上の記述は間違っています。**修正は以下の通りです。
s
をt
に代入コピーする段階で、内部的にs
のポインタまでコピーしてしまうことが起こっています。
これにより、t
はs
と同じメモリ領域の配列(つまり、同じ配列)を参照している状態になってしまうため、どちらか一方を変更したら両方とも変更されてしまう…といったことが起こるようです。
これに対して、copy関数を使った場合は、新しいメモリ領域に新しい配列を作成して、全く別の配列を参照する形になります。これによって、別個の配列として扱えるそうです。
詳しくは、この記事のコメント欄で@c-yanさんが説明して下さっていますので、ぜひ見て頂けると分かりやすいかと思います。