Нарезка указателя фрагмента, переданного в качестве аргумента

У меня есть следующий код:

func main() {
    var buf []byte{1, 2, 3, 4, 5}
    buf = buf[2:]
    fmt.Println(buf)
    panic(1)
}

Однако я хочу передать указатель на buf срезать байт для другой функции и нарезать ее там, так что-то вроде:

func main() {
    var buf []byte{1, 2, 3, 4, 5}
    sliceArr(&buf, 2)
    fmt.Println(buf)
    panic(1)
}

func sliceArr(buf *[]byte, i int) {
    *buf = *buf[i:]
}

Это дает мне ошибку, что я не могу использовать тип []byte как тип *[]byte в аргументе sliceArr() функция, и что я не могу нарезать тип *[]byte, В чем дело? Разве фрагменты не передаются по ссылке по умолчанию? Я пытался сделать это без указателя, но это не работает - массив копируется. Как я могу это сделать?

1 ответ

Решение

Ошибка cannot use type []byte as type *[]byte in argument to sliceArr() исходит из опечатки, которую вы не опубликовали (вы пытались передать фрагмент, а не указатель на фрагмент sliceArr()).

Что касается другой ошибки (cannot slice type *[]byte), вам просто нужно использовать скобки для группировки разыменования указателя:

*buf = (*buf)[i:]

И вы случайно пропустили = знак из объявления переменной. Кроме этого, все работает так, как вы написали:

func main() {
    var buf = []byte{1, 2, 3, 4, 5}
    sliceArr(&buf, 2)
    fmt.Println(buf)
}

func sliceArr(buf *[]byte, i int) {
    *buf = (*buf)[i:]
}

Вывод (попробуйте на Go Playground):

[3 4 5]

Замечания:

Обратите внимание, что в спецификации говорится, что если p это указатель на массив, p[low:high] это сокращение для (*p)[low:high] то есть указатель автоматически разыменовывается для вас. Это не происходит автоматически, если p указатель на срез, p[low:high] неверно, так как вы не можете нарезать указатель. Таким образом, вы должны разыменовать указатель вручную в случае указателей на фрагменты.

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

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

Заметка 2:

Если тип параметра представляет собой срез (а не указатель на срез), и вы передаете срез, базовый массив не копируется, только заголовок среза. Но в этом случае, если вы срезаете аргумент (который является копией заголовка среза) и присваиваете новый срез (заголовок среза) этому аргументу, вы просто меняете значение параметра (локальной переменной) и не оригинальное значение (переменная), которое было передано, поэтому оно не будет работать.

Прочитайте следующие сообщения в блоге для более подробной информации о ломтиках:

Go Slices: использование и внутренности

Массивы, срезы (и строки): механика "добавления"

Другие вопросы по тегам