Почему я не могу добавить значение к фрагменту структуры, используя ссылку? - PullRequest
0 голосов
/ 01 декабря 2019

Пойду, я предположил, что срезы были переданы по ссылке, но, похоже, это работает для значений, но не для самого массива. Например, если у меня есть эта структура:

    l := Line{
        Points: []Point{
            Point{3, 4},
        },
    }

, я могу определить переменную, которая получает ссылку на срез структуры

slice := l.Points

И затем, если я изменяю ее,исходная структура, на которую ссылается переменная, будет отражать эти изменения.

slice[0].X = 1000
fmt.Printf(
    "This value %d is the same as this %d", 
    slice[0].X, 
    l.Points[0].X,
)

Это отличается от поведения массивов, которые, я полагаю, передаются по значению. Так, например, если бы я определил предыдущий код, используя массив:

l := Line{
    Points: [1]Point{
        Point{3, 4},
    },
}
arr := l.Points
arr[0].X = 1000
fmt.Println(arr.[0].X != s.Points[0].X) // equals true, original struct is untouched

Тогда структура l не изменилась бы.

Теперь, если я хочу изменить сам слайс, я, очевидно, не могу сделать это:

slice = append(slice, Point{99, 100})

Так как это только переопределит переменную слайса, потеряв исходную ссылку. Я знаю, что могу просто сделать это:

l.Points = append(l.Points, Point{99, 100})

Но в некоторых случаях удобнее иметь другую переменную вместо того, чтобы вводить все это.

Я попробовал это:

*slice = append(*slice, Point{99, 100})

Но это не работает, так как я пытаюсь разыменовать что-то, что явно не является указателем.

Я наконец попробовал это:

slice := &l.Points

*slice = append(l.Points, Point{99, 100})

И это работает, но я не уверен, что происходит. Почему значение среза не перезаписывается? Как здесь работает append?

1 Ответ

2 голосов
/ 01 декабря 2019

Append возвращает новый фрагмент, который может изменить исходный массив основы исходного фрагмента. Исходный фрагмент все равно будет указывать на исходный массив резервных копий, а не на новый (который может находиться или не находиться в одном и том же месте в памяти)

Например ( детская площадка )

slice := []int{1,2,3}
fmt.Println(len(slice))
// Output: 3
newSlice := append(slice, 4)
fmt.Println(len(newSlice))
// Output: 4
fmt.Println(len(slice))
// Output: 3 

Хотя срез можно описать как «толстый указатель на массив», он не является указателем, и поэтому вы не можете разыменовать его, поэтому вы получаете ошибку.

Создав указатель на срез и используя append, как вы делали выше, вы устанавливаете срез, на который указывает указатель, на «новый» срез, возвращаемый append.

Для получения дополнительной информации, проверьтеout Использование Go Slice And Internals

...