Удаление элемента из слайса

Объясните поведение во второй строке. Изначально было два варианта:

  • Вывод не изменится во втором случае
  • Вывод изменится и последний элемент будет отсутствовать

Но оказался другой вариант. Как объяснить?

// You can edit this code!
// Click here and start typing.
package main

import "fmt"

type Item struct {
    A int
    B string
}

func remove(slice []*Item, s int) []*Item {
    return append(slice[:s], slice[s+1:]...)
}

func main() {
    x := make([]*Item, 0)
    x = append(x, &Item{1, "2"})
    x = append(x, &Item{2, "3"})
    x = append(x, &Item{3, "4"})
    x = append(x, &Item{4, "5"})
    fmt.Println(x[0], x[1], x[2], x[3])
    remove(x, 0)
    fmt.Println(x[0], x[1], x[2], x[3])
    x = remove(x, 3)
    fmt.Println(x[0], x[1], x[2])
}

&{1 2} &{2 3} &{3 4} &{4 5}

&{2 3} &{3 4} &{4 5} &{4 5}

&{2 3} &{3 4} &{4 5} // тут была бы паника для третьекго элемента

Program exited.


Ответы (1 шт):

Автор решения: Pak Uula

Руководство по языку Go, раздел Appending and copying slices

If the capacity of s is not large enough to fit the additional values, append allocates a new, sufficiently large underlying array that fits both the existing slice elements and the additional values. Otherwise, append re-uses the underlying array.

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

Поэтому, когда вы вызвали remove(x,0), операции производились на массиве, на который указывает x. В нем элементы с позиций 1,2,3 были скопированы на позиции 0,1,2. Элемент на позиции 3 остался доступным, так как в дескрипторе массива x написано "в массиве четыре элемента".

После вызова x = remove(x,3) в переменную x сохранён новый дескриптор массива, в котором написано "в массиве три элемента". В результате последний элемент перестал быть видным.

→ Ссылка