Добавление к срезу на структуре внутри другого среза не сохраняется - PullRequest
0 голосов
/ 15 октября 2018

Например:

package main

import "fmt"

type Test struct {
  elems []string
}

func main() {
  initial := Test{
    elems: make([]string, 0),
  }
  initial.elems = append(initial.elems, "apple")
  fmt.Println(initial.elems) // #1 [apple]

  s := make([]Test, 0)
  s = append(s, initial)

  initial.elems = append(initial.elems, "bannana")
  fmt.Println(initial.elems) // #2 [apple bannana]
  fmt.Println(s[0].elems) // #3 [apple]

  second := s[0]
  second.elems = append(second.elems, "carrot")
  fmt.Println(second.elems) // #4 [apple bannana]
}

Я ищу помощь в понимании операторов печати № 3 и № 4.В # 3 я ожидаю [apple bannana], а в # 4 я ожидаю [apple bannana carrot].

Насколько я понимаю, поле elems, являющееся слайсом, автоматически передается по ссылке, и поэтому каждый из них добавляет, что яdo в приведенном выше блоке кода должен изменить базовый массив.Но, очевидно, это не так.

Итак, мой вопрос: что происходит с initial, когда он вставляется в срез, который делает это неработоспособным?Кроме того, как написать этот код, чтобы получить ожидаемый результат в операторе печати # 4?

Ответы [ 2 ]

0 голосов
/ 15 октября 2018

В Golang упоминается:

Значения карты и среза ведут себя как указатели: они являются дескрипторами, которые содержат указатели на базовую карту или данные среза.Копирование значения карты или фрагмента не копирует данные, на которые он указывает.

Способ добавления фрагмента s - создание нового фрагмента путем добавления копии структуры Test вs ломтик.Следовательно, вы не устанавливаете указатель на исходную структуру Test.Так что если данные изменяются внутри структуры, они также будут отражены в срезе.Это проблема, с которой вы сталкиваетесь.

  initial.elems = append(initial.elems, "apple")
  fmt.Println(initial.elems) // #1 [apple]

  s := make([]Test, 0) // this should be pointer to the struct to have teh changes in future to original struct.
  s = append(s, initial) // appending to the s slice

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

package main
import "fmt"

type Test struct {
  elems []string
}

func main() {
  initial := Test{
    elems: make([]string, 0),
  }
  initial.elems = append(initial.elems, "apple")
  fmt.Println(initial.elems) // #1 [apple]

  s := make([]*Test, 0) // create a pointer to Test struct.
  s = append(s, &initial)

  initial.elems = append(initial.elems, "bannana")
  fmt.Println(initial.elems) // #2 [apple bannana]
  fmt.Printf("%+v\n",*s[0]) // #3 [apple banana]

  second := s[0]
  second.elems = append(second.elems, "carrot")
  fmt.Println(second.elems) // #4 [apple bannana carrot]
}

Вывод: -

[apple]
[apple bannana]
{elems:[apple bannana]}
[apple bannana carrot]

Рабочий код на Go Playground

0 голосов
/ 15 октября 2018

Это связано с тем, что переменная initial отличается от s[0] - это две независимые переменные Test, и добавление к одной не меняет второе.initial копируется по значению в другой объект при передаче в append()

Доказательство:

fmt.Printf("second: %p, initial: %p\n", &second.elems[0], &initial.elems[0])

(где second.elems[0] == "apple" и initial.elems[0] == "apple")выводит

second: 0xc00000a120, initial: 0xc00000a0c0

, который показывает, что это правда

...