Слайс может получить доступ к другому слайсу за пределами диапазона, но индексация вне диапазона вызывает панику

Мой код:

package main

import (
    "fmt"
)

func main() {
    a := [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
    b := a[1:4]
    fmt.Println("a:", a)
    fmt.Println("b:", b)
    
    // Works fine even though c is indexing past the end of b.
    c := b[4:7]
    fmt.Println("c:", c)
    
    // This fails with panic: runtime error: index out of range [4] with length 3
    // d := b[4]
}

Выход:

a: [0 1 2 3 4 5 6 7 8 9]
b: [1 2 3]
c: [5 6 7]

Если я раскомментирую строку, содержащую d := b[4], это приведет к этой ошибке:

panic: runtime error: index out of range [4] with length 3

Мой вопрос:

Почему можно получить доступ к b[4:7], даже если индекс 4 находится за пределами допустимого диапазона для b, длина которого равна 3, но нельзя получить доступ к b[4]? Какие правила языка Go объясняют такое поведение?


person Lone Learner    schedule 11.08.2020    source источник
comment
Почему минусы? Что я могу сделать, чтобы улучшить этот вопрос?   -  person Lone Learner    schedule 11.08.2020
comment
Почему есть закрытое голосование с причиной Невоспроизводимо или вызвано опечаткой? Какая часть моего вопроса не воспроизводится?   -  person Lone Learner    schedule 11.08.2020


Ответы (1)


Соответствующие правила: Спецификация: индексные выражения и Спецификация: выражения среза.

Вкратце: при индексировании индекс должен быть меньше длины. При нарезке верхний индекс должен быть меньше или равен емкости.

При индексации: a[x]

индекс x в диапазоне, если 0 <= x < len(a), в противном случае он вне диапазона

При нарезке: a[low: high]

Для массивов или строк индексы в пределах диапазона, если 0 <= low <= high <= len(a), иначе они за пределами диапазона. Для срезов верхней границей индекса является емкость среза cap(a), а не его длина.

Когда вы делаете это:

b := a[1:4]

b будет фрагментом, разделяющим резервный массив с a, длина b будет 3, а его емкость будет 9. Таким образом, позже вполне допустимо нарезать b даже за пределы его длины, вплоть до его емкости, которая равна 9. Но при индексировании вы всегда можете индексировать только часть, охватываемую длиной среза.

Мы используем индексирование для доступа к текущим элементам среза или массива, и мы используем срезы, если хотим создать фрагмент массива или среза или если мы хотим его расширить. Расширение означает, что нам нужна большая часть (но то, что все еще покрывается резервным массивом).

person icza    schedule 11.08.2020
comment
@lone-learner, обязательное чтение: это, затем это, и вы будете на 100 % защищены от срезов. - person kostix; 11.08.2020
comment
icza, kostix: Спасибо за ссылки на спецификацию и Go Blog. Я все еще изучаю Го из Путешествия по Го. Я тоже прочитаю ваши ссылки после успешного завершения тура. - person Lone Learner; 12.08.2020