panic: runtime error: index out of range とは
このGo言語のパニックエラーは、スライスや配列に対して有効な範囲外のインデックスを使用して要素にアクセスしようとしたときに発生します。Goのスライスや配列のインデックスは0から始まり、その長さの1未満までが有効範囲です。このエラーは、ループの条件ミスや、スライスの長さが想定と異なる場合に頻繁に遭遇します。
エラーの発生パターン
このエラーは主に以下のようなケースで発生します。
パターン1: ループの境界条件ミス
package main
import "fmt"
func main() {
numbers := []int{10, 20, 30}
for i := 0; i <= len(numbers); i++ { // len(numbers) は 3
fmt.Println(numbers[i]) // i=3 の時に panic: index out of range [3] with length 3
}
}
for i := 0; i <= len(numbers); i++というループ条件では、iがスライスの長さ(この場合は3)と等しい場合にもアクセスを試みてしまいます。Goのインデックスは0からlen-1までなので、numbers[3]は存在せずエラーになります。
package main
import "fmt"
func main() {
numbers := []int{10, 20, 30}
for i := 0; i < len(numbers); i++ { // i < len(numbers) に修正
fmt.Println(numbers[i])
}
}
パターン2: 固定長配列の範囲外アクセス
package main
import "fmt"
func main() {
var fixedArray [3]string // 長さ3の配列
fixedArray[0] = "apple"
fixedArray[1] = "banana"
fixedArray[2] = "cherry"
fmt.Println(fixedArray[3]) // panic: runtime error: index out of range [3] with length 3
}
Goの固定長配列もスライスと同様に0からlen-1の範囲でインデックスアクセスが可能です。この例では、長さ3の配列に対してインデックス3でアクセスしようとしています。これは配列の有効な範囲外であるため、パニックが発生します。
package main
import "fmt"
func main() {
var fixedArray [3]string
fixedArray[0] = "apple"
fixedArray[1] = "banana"
fixedArray[2] = "cherry"
fmt.Println(fixedArray[2]) // 有効なインデックスでアクセス
}
パターン3: 空のスライスへのアクセス
package main
import "fmt"
func main() {
var emptySlice []string // 空のスライス
// スライスが空であることを確認せずにアクセス
fmt.Println(emptySlice[0]) // panic: runtime error: index out of range [0] with length 0
}
空のスライスemptySliceは要素を一つも持っていません。したがって、インデックス0でアクセスしようとすると、有効な範囲外となりパニックが発生します。アクセスする前にスライスが空でないことを確認する必要があります。
package main
import "fmt"
func main() {
var emptySlice []string
if len(emptySlice) > 0 { // スライスが空でないことを確認
fmt.Println(emptySlice[0])
} else {
fmt.Println("Slice is empty, cannot access index 0.")
}
}
根本原因の特定方法
エラーが発生した箇所で、スライスや配列のlen()と、アクセスしようとしているインデックスの値をfmt.Println()やデバッガを使って出力し、比較することで問題を特定できます。これにより、インデックスが有効範囲内にあるべきか、またはループの条件が正しくないかを確認できます。特に、ループを使用している場合は、ループ変数の値がどのように変化しているかを追跡することが重要です。
package main
import "fmt"
func main() {
numbers := []int{10, 20, 30}
for i := 0; i <= len(numbers); i++ {
fmt.Printf("Attempting to access index %d (slice length: %d)\n", i, len(numbers))
// エラーが発生する行
// fmt.Println(numbers[i])
}
}
防止策とベストプラクティス
このエラーを防ぐ最も確実な方法は、スライスや配列にアクセスする前に、常にlen()関数を使ってインデックスが有効範囲内にあることを確認することです。ループでスライスを処理する場合は、Goのrangeキーワードを使用すると、インデックスが自動的に正しい範囲に制約されるため安全です。外部からの入力に基づいてインデックスを計算する場合は、特に注意が必要です。
package main
import "fmt"
func main() {
numbers := []int{10, 20, 30}
// range を使った安全なイテレーション
for i, num := range numbers {
fmt.Printf("Index: %d, Value: %d\n", i, num)
}
// 特定のインデックスにアクセスする前にチェック
idx := 3 // 意図的に範囲外のインデックス
if idx >= 0 && idx < len(numbers) {
fmt.Printf("Value at index %d: %d\n", idx, numbers[idx])
} else {
fmt.Printf("Index %d is out of range (length: %d)\n", idx, len(numbers))
}
}
よくある質問(FAQ)
-
Q
nilスライスと空スライスの違いは? -
A
nilスライスは宣言されただけで初期化されていないスライスで、基底配列への参照も長さも容量も0です。一方、空スライスはmake([]int, 0)や[]int{}のように初期化されており、長さと容量は0ですが、基底配列への参照は存在します。どちらも要素を持たないため、index out of rangeエラーを引き起こす可能性があります。
-
Qこのエラーは配列でも発生しますか?
-
A
はい、固定長配列でも
index out of rangeエラーは発生します。配列のインデックスもスライスと同様に0からlen-1までの範囲であるため、この範囲外にアクセスしようとするとパニックになります。Go言語ではスライスがより柔軟で頻繁に使用されますが、配列を使用する場合も同様の注意が必要です。
-
Qマップ(map)でこのエラーが発生することはありますか?
-
A
マップ(map)では、キーが存在しない場合に
panic: runtime error: index out of rangeは発生しません。代わりに、キーが存在しない場合はゼロ値(数値型なら0、文字列型なら空文字列など)が返されます。ただし、マップの値がスライスや配列である場合に、そのスライスや配列に対して誤ったインデックスでアクセスすると、このエラーが発生する可能性はあります。




コメント