打ち首こくまろ

限界オタクの最終処分場

append()はスライスを書き換える(当たり前)

プロプログラマの皆様に於かれましては「何を当たり前のことを。。。」と思われるかもしれないが、仕事中めっちゃ詰まったので自分のためにメモ。

[ま か べ み ず き] というスライスの先頭から5文字を取り出し、その末尾に をくっつけたかったので、以下のコードを書いた。

mizuki := []string{"ま", "か", "べ", "み", "ず", "き"}
fmt.Println(mizuki)
// -> [ま か べ み ず き]
fmt.Println(append(mizuki[:5], "な"))
// -> [ま か べ み ず な]

一件落着と言いたいところだが、このコードではappend後、mizukiの中身までも書き換わってしまう。

fmt.Println(mizuki)
// ->[ま か べ み ず な]

考えてみれば当たり前のことで、append()はスライスに要素を追加するメソッドで、要素を追加したスライスを返してくれるメソッドではない。 のすぐ後ろに を追加しようとしたところ、を上書きしてしまった、んだと思う。

(詳しいことは公式ドキュメントに書かれてそうだが、ちょっと読むのめんどいので略)

対策としては、copy()を使ってスライスをコピーしてからappendする。

mizuki := []string{"ま", "か", "べ", "み", "ず", "き"}
fmt.Println(mizuki)
// -> [ま か べ み ず き]
mizu := make([]string, 5)
copy(mizu, mizuki[:5])
fmt.Println(append(mizu, "な"))
// -> [ま か べ み ず な]
fmt.Println(mizuki)
// -> [ま か べ み ず き]

素晴らしい。

ここにも罠があって、コピー先のスライス(ここではmizu)が十分な容量で初期化されていないと、コピーがうまくいかない。

mizuki := []string{"ま", "か", "べ", "み", "ず", "き"}

var ma []string
copy(ma, mizuki[:5])
fmt.Println(ma)
// -> []

kabe := make([]string, 3)
copy(kabe, mizuki[:5])
fmt.Println(kabe)
// -> [ま か べ]

Go言語には罠がいっぱい!