Подтвердить что ты не робот

Определение функции golang struct с помощью указателя или нет

Может кто-нибудь объяснить мне, почему добавление к массиву работает, когда вы это делаете:

func (s *Sample) Append(name string) {
    d := &Stuff{
        name: name,
    }
    s.data = append(s.data, d)
}

Полный код здесь

Но не тогда, когда вы это сделаете:

func (s Sample) Append(name string) {
    d := &Stuff{
        name: name,
    }
    s.data = append(s.data, d)
}

Есть ли вообще какая-то причина, почему вы хотите использовать второй пример.

4b9b3361

Ответ 1

Как упоминалось в FAQ

Должен ли я определять методы значений или указателей?

func (s *MyStruct) pointerMethod() { } // method on pointer
func (s MyStruct)  valueMethod()   { } // method on value

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

В приведенных выше примерах, если pointerMethod изменяет поля s, вызывающий будет видеть эти изменения, но valueMethod вызывается с копией аргумента вызывающего (что определение передачи значения), поэтому изменения, которые он делает, будут невидимы для вызывающего.

В вашем случае func (s Sample) Append(name string) изменяет копию.

laher напоминает нам в комментариях, что использование значения вместо указателя также означает получение копировать и уважать неизменный характер объекта:

Вы хотите использовать non-pointer valueMethod, когда (для nstance) вы возвращаете частное свойство [value, полученное из] 'неизменяемого'.

См. "Почему приемники проходят по значению в Go?":

Может быть полезно, если, например, у вас есть маленький неизменяемый объект. Вызывающий может точно знать, что этот метод не изменяет его. Они не могут знать это, если приемник является указателем, не читая сначала код.

Ответ 2

Ломки Go - хитрый зверь. Внутренне переменная типа среза (например, []int) выглядит так:

struct {
    data *int // pointer to the data area
    len  int
    cap  int
}

Когда вы передаете срез функции, эта структура передается по значению, тогда как базовая область данных (то есть, что указывает data) не копируется. Встроенная функция append() изменяет область data (или генерирует новую) и возвращает новый срез с обновленными значениями len, data и cap. Если вы хотите переписать все, что не входит в базовую область данных, вам нужно передать указатель на срез или вернуть измененный фрагмент.

Ответ 3

Go передает аргументы по значению, а не по ссылке, если вы не используете указатель. Поэтому внутри функции вы не изменяете s в любой внешней области, если вы просто передаете значение. Однако, когда вы передаете указатель, вы можете изменить "реальную" переменную, а не только копию, которая существует внутри функции.