注意事項
https://qiita.com/ysti/items/970dddb041a72e6efdb6#comment-92d43db90663c2b646fc
こちらでご指摘いただいている通り、
append が再確保していないことを前提にするのは、特段の事情がない限りおすすめしません。
ということですのでご注意くださいませ。
Goのスライスでハマった(定期)
スライスを関数に渡して関数内で長さが変わって増えた分が呼び出し元で見れない問題にハマった。
結論としては、長さを変えてやればOKだった。(変えれないと思っていたら変えれた)
package main
import "fmt"
func main() {
data := make([]byte, 0, 16)
data = append(data, []byte("abcd")...)
fmt.Println("1 ", string(data))
newLen := repeater(data)
fmt.Println("3 ", string(data)) // ここでは足されたデータが見えなーい
data = data[0:newLen] // 長さを変えると、世界が変わるー (cap超えぬようにね)
fmt.Println("4 ", string(data))
}
// 同じデータを繰り返して足す。長さが倍になる。
func repeater(data []byte) int {
data = append(data, data...)
fmt.Println("2 ", string(data))
return len(data)
}
// 結果
// 1 abcd
// 2 abcdabcd
// 3 abcd
// 4 abcdabcd
メモ
-
append
はcap
を超えると新しい配列になる。(基本だけど念の為。) - スライスの構造 (とてもわかりやすい!)
- 落とし穴 (穴があったら落ちたいよね)
-
copy
-
copy(dst, src)
はsrc
のlen(src)
分をコピー。 -
len(src)
がゼロの場合はコピーされない。
-
- スライスへのアクセス
- 長さゼロのスライスにアクセスしようとするとキャパシティが足りていてもエラーとなる。
-
lenは更新できる。
-
slice = slice[0:newLen]
でnewLen
まで使えるようになる。-
注意
cap
を超えるとエラーになるので実際にはチェックが必要。
-
注意
-
- スライスを関数に渡す場合
- 配列への参照がコピーされるため関数側で書き換えると呼び出し側にも反映される。
-
関数内で長さが変わっても呼び出し元のスライスの
len
は変わらず、増えたデータへアクセスできない。-
長さを変えればOK。(
slice = slice[0:newLen]
)
-
長さを変えればOK。(
package main
import "fmt"
func main() {
s := make([]byte, 0, 16)
// s[0xc00001c030]:[]byte{}, len:0 cap:16
_ = s[0] // panic: runtime error: index out of range [0] with length 0
copy(s, []byte("abcd"))
// s[0xc00001c030]:[]byte{}, len:0 cap:16 // no change.
_ = s[0] // panic: runtime error: index out of range [0] with length 0
s = s[0:4] // change len.
// s[0xc00001c030]:[]byte{0x0, 0x0, 0x0, 0x0}, len:4 cap:16 // no data copied.
_ = s[0] // OK.
_ = s[4] // panic: runtime error: index out of range [4] with length 4
copy(s, []byte("abcd")) // copy again.
// s[0xc00001c030]:[]byte{0x61, 0x62, 0x63, 0x64}, len:4 cap:16 // data copied.
efgh(s)
// s[0xc00001c030]:[]byte{0x61, 0x62, 0x63, 0x64}, len:4 cap:16 // no change.
s = s[0:8] // change len.
// s[0xc00001c030]:[]byte{0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x64, 0x68}, len:8 cap:16 // as expected.
}
func efgh(s []byte) {
// s[0xc00001c030]:[]byte{0x61, 0x62, 0x63, 0x64}, len:4 cap:16
s = append(s, []byte("efdh")...)
// s[0xc00001c030]:[]byte{0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x64, 0x68}, len:8 cap:16
}
解決のヒントは本家のEffective Goというね。さすが。
https://go.dev/doc/effective_go#slices