0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

GolangのSlice挙動メモ:copy, append, len, cap, 関数に渡して変更したときどうなるか。

Last updated at Posted at 2022-09-16
注意事項

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

メモ

  • appendcapを超えると新しい配列になる。(基本だけど念の為。)
  • スライスの構造 (とてもわかりやすい!)
  • 落とし穴 (穴があったら落ちたいよね)
  • copy
    • copy(dst, src)srclen(src)分をコピー。
    • len(src)がゼロの場合はコピーされない。
  • スライスへのアクセス
    • 長さゼロのスライスにアクセスしようとするとキャパシティが足りていてもエラーとなる。
    • lenは更新できる。
      • slice = slice[0:newLen]newLenまで使えるようになる。
        • 注意 capを超えるとエラーになるので実際にはチェックが必要。
  • スライスを関数に渡す場合
    • 配列への参照がコピーされるため関数側で書き換えると呼び出し側にも反映される。
    • 関数内で長さが変わっても呼び出し元のスライスのlenは変わらず、増えたデータへアクセスできない。
      • 長さを変えればOK。slice = slice[0:newLen]
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

0
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?