Почему при append к срезу y изменяется x, но не полностью

package main 

import "fmt"

func main() {
    x := make([]int, 0, 5)
    x = append(x, 1, 2, 3, 4)
    y := x[:2]
    z := x[2:]
    fmt.Println("x:", x)
    fmt.Println("y:", y)
    fmt.Println("z:", z)
    fmt.Println(cap(x), cap(y), cap(z))
    y = append(y, 30, 40, 50)
    fmt.Println("y:", y)
    fmt.Println("x:", x)
    //x = append(x, 60)
    //z = append(z, 70)
    //fmt.Println("x:", x)
    //fmt.Println("y:", y)
    //fmt.Println("z:", z)
}

Получается изначально при x := make([]int, 0, 5) у нас создаётся массив типа [5]int{0,0,0,0,0} ёмкость (cap) которого равна 5 и на него ссылается срез x. Затем мы заполняем срез x = append(x, 1, 2, 3, 4) таким образом его len = 4, а cap = 5 по исходному массиву. Полсе этого создаём срез срезов y который выглядит как [1,2] с длиной 2 и ёмкостью 5, так как всё ещё ссылается на исходный массив с cap=5.

Вопрос начинается здесь y = append(y, 30, 40, 50). Получается если у среза y len=2 и cap=5 то при выводе y в консоль мы получаем логичный результат: y: [1 2 30 40 50]. Так как x и y ссылаются на один и тот же массив, я ожидаю, что срез x, станет таким же как и срез y, объясню почему я так думаю: сейчас в срезе x лежат следующие элементы [1,2,3,4]. Так как и x и y ссылаются на одно и тоже, следовательно y = append(y, 30, 40, 50) должно положить в y (30, 40, 50), перезаписать цифры 3,4 в срезе x на цифры 30,40 плюс добавить в x ещё одно число 50, ведь его cap=5 позволяет это сделать без переалокации. Но, после всех проделанных операций, при выводе среза x получаем следующий результат: x: [1 2 30 40]. Почему вывод такой? Куда делось 50? Объясните пожалуйста.


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

Автор решения: Uranus

плюс добавить в x ещё одно число 50, ведь его cap=5 позволяет это сделать без переалокации

У вас ошибка в этой части рассуждений. Операция append действительно добавляет число 50 в общий массив, так как срез y способен вместить все добавленные элементы без переаллокации. Но, операция append применённая к срезу y никак не влияет на срез x, то есть не может изменить его len=4. Вот почему при выводе x число 50 не отображается.

→ Ссылка
Автор решения: Pak Uula

Срез - это структура данных из трёх полей: {array, len, cap}

  • array это указатель на данные
  • len число объектов в срезе
  • cap максимальное число объектов, которые может вместить срез без переаллокации.

Когда вы создали y, вы создали новый объект {x.array, 2, x.cap}. Меняя содержимое y, вы изменили память по указателю y.array, и поле y.len. Так как y.array == x.array, то данные в срезе x изменились, но! остальные поля в x остались теми же. x.len как был 4, так и остался.

→ Ссылка