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

19

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

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)
}

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

    
задан Jacob 19.08.2014 в 13:19
источник

3 ответа

35

Как упоминалось в 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 напоминает нам в комментариях , что использование значения вместо указателя также означает получение копии и соблюдение неизменяемого природы объекта ::

  

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

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

  

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

    
ответ дан VonC 19.08.2014 в 13:25
источник
  • Отличная информация, но это не предполагает ситуации, когда вы захотите использовать не указатель valueMethod. Я могу вспомнить один пример - когда вы возвращаете частное свойство [value, полученное из] 'неизменяемого', было бы более ясным показать, что он не является мутационным с помощью метода, отличного от указателя. –  laher 23.08.2014 в 00:01
  • @laher хорошая точка. Я включил ваш комментарий в ответ для большей видимости, и я добавил ссылку / ссылку. –  VonC 23.08.2014 в 00:18
  • Хороший, хорошо выглядящий. Мне было интересно об этом какое-то время, и мне было бы интересно узнать о других случаях использования. Думаю, вы могли бы сказать, что это позволяет избежать явной «защитной копии», но это лишь часть той же неизменяемости. Cheers @VonC –  laher 23.08.2014 в 00:59
7

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

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

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

    
ответ дан fuz 19.08.2014 в 13:53
источник
1

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

    
ответ дан Todd A. Jacobs 19.08.2014 в 13:25
источник