Goのスライスは、言語の最も強力で、同時に最も危険なツールの1つです。一見すると、単なる「動的配列」のように見えますが、その下には十分なニュアンスが隠されており、1行のコードがバグになり、デバッグには数時間かかります。実際にどのように機能するかを見てみましょう。

スライスの構成
Goのスライスは配列ではありません。これは、以下を保存する「薄い包み紙」です。
配列ポインタ — データの実際の保存場所。
長さ(len) — 現在利用可能な要素の数。
容量(キャップ) - メモリを再割り当てせずに収容できる要素の数。
s := []int{1, 2, 3, 4}
fmt.Println(len(s)) // 4
fmt.Println(cap(s)) // 4
キャパシティとその役割
容量は在庫です。appendを実行すると、Goは以下をチェックします。
容量が十分な場合、新しい要素は同じ配列に書き込まれます。
キャパシティがなくなった場合、Goは 新しいより大きな配列 古いデータをそこにコピーします。
s := make([]int, 2, 4) // len=2, cap=4
s = append(s, 10, 20) // まだ空きがあります
fmt.Println(cap(s)) // 4
s = append(s, 30) // キャップがなくなりました → 新しい配列
fmt.Println(cap(s)) // 8コピーと落とし穴
copy関数は、表面コピーを実行します。
a := []int{1, 2, 3}
b := make([]int, len(a))
copy(b, a)
b[0] = 99
fmt.Println(a) // [1 2 3]
fmt.Println(b) // [99 2 3]しかし、単に割り当てる場合:
a := []int{1, 2, 3}
b := a
b[0] = 99
fmt.Println(a) // [99 2 3] ❗ここでは、a と b は同じ配列を指しています。これは初心者がよく犯す間違いです。

成長:スライスの成長方法
Goは正確な成長アルゴリズムを保証するものではありませんが、通常は次のように動作します。
容量が小さい場合は2倍になります。
容量がすでに大きい場合は、約25%増加します。
👉したがって、ビッグデータを扱う場合は、必要なマージンを使用してmake([]T, 0, N)を事前に割り当てることをお勧めします。
落とし穴:開発者が最も頻繁に燃える場所
一般的な基礎配列
a := []int{1, 2, 3, 4, 5} b := a[:3] c := a[2:] c[0] = 99 fmt.Println(b) // [1 2 99] 😱bとcは同じ配列を使用します。成長は「繋がり」を壊します
a := []int{1, 2, 3} b := a a = append(a, 4) // キャップがなくなりました → 新しい配列 b[0] = 99 fmt.Println(a) // [1 2 3 4] fmt.Println(b) // [99 2 3]成長後、
aはすでに別の配列を持ち、bとの関係は失われました。大きな配列のスライス
big := make([]byte, 1e6) small := big[:1] // smallは小さく見えますが、メモリ内のすべてのメガバイトを保持します解決策:
copyを新しいスライスに。
合計
Go のスライスは便利なツールですが、
容量と成長を忘れないでください。
独立したデータが必要な場合は
copyを使用してください。「カットからのカット」には注意してください。
内部の仕組みを理解することで、予期しないバグを回避し、より信頼性の高いコードを書くことができます。
