Go のスライスと配列の違い

Go には配列とスライスという2つの似たデータ構造がありますが、実際の開発ではスライスを使う場面がほとんどです。両者の違いを正しく理解しておくことが重要です。

固定長と可変長

配列はサイズが型の一部であり、宣言時に決めたサイズを変更できません。一方、スライスはサイズを自由に増減できます。

配列

サイズが固定。[3]int[5]int は別の型として扱われる。

スライス

サイズが可変。append で要素を追加でき、サイズが自動的に拡張される。

// 配列:サイズは型の一部
var arr1 [3]int
var arr2 [5]int
// arr1 = arr2 // コンパイルエラー:型が異なる

// スライス:サイズは型に含まれない
var slice1 []int
var slice2 []int
slice1 = slice2 // OK

関数への渡し方

配列を関数に渡すと、値がコピーされます。一方、スライスは内部で配列への参照を持っているため、コピーは軽量で、変更が呼び出し元にも反映されます。

func modifyArray(arr [3]int) {
    arr[0] = 999
}

func modifySlice(s []int) {
    s[0] = 999
}

func main() {
    arr := [3]int{1, 2, 3}
    modifyArray(arr)
    fmt.Println(arr[0]) // 1(変更されない)

    slice := []int{1, 2, 3}
    modifySlice(slice)
    fmt.Println(slice[0]) // 999(変更される)
}

配列の場合は全要素がコピーされるため、大きな配列を関数に渡すとパフォーマンスに影響します。

宣言時の構文

見た目は似ていますが、[] の中にサイズを書くかどうかで区別されます。

構文種類説明
[3]int配列サイズ3の固定長配列
[]intスライス可変長のスライス
[...]int{1,2,3}配列要素数から自動推論

[...] 構文は配列の省略記法で、初期化時の要素数に基づいてサイズが決まります。これもスライスではなく配列です。

いつ配列を使うか

実際のコードではスライスを使うことがほとんどですが、以下のような場合には配列が適しています。

サイズが固定で変わらないことを型で保証したい場合(例:RGB の 3 要素)
暗号化やハッシュなど、固定サイズのバイト列を扱う場合
パフォーマンスが極めて重要で、ヒープ割り当てを避けたい場合

基本的には「迷ったらスライスを使う」と覚えておけば問題ありません。