2015-04-24 2 views
5

Я думаю, что это будет немного очевидно после того, как оно будет вызвано мне, но сейчас следующее не щелкает.Ссылка на указатель и срез - приемник

type Stack []interface{} 

func (stack *Stack) Push(x interface{}) { 
    *stack = append(*stack, x) 
} 

У меня есть тип под названием Stack, который представляет собой кусок пустых интерфейсов. Учитывая, что он пуст, метод Push удовлетворяет интерфейсу. Учитывая, что срез является ссылочным типом, почему «приемник стека» не может быть передан по значению? Более того, в приведенном выше примере приемник передается в качестве указателя, почему добавление встроенного компонента необходимо снова передать указателем?

IE почему бы не работать, учитывая, что срез является ссылочным указателем на базовый массив?

func (stack Stack) Push(x interface{}) { 
    stack = append(stack, x) 
} 

ответ

7

См. Это article on the Go blog. Он подробно объясняет, что происходит и полностью отвечает на ваш вопрос.

Из раздела Попутных ломтиков к функциям:

Важно понимать, что даже если кусочек содержит указатель, он сам по себе является ценностью. Под обложками это значение структуры, содержащее указатель и длину. Это не указатель на структуру.

В результате вам понадобится приемник указателя или вам нужно вернуть срез в качестве значения, если вы хотите его изменить append.

Если вы просто хотите изменить содержимое ломтика вы можете просто передать кусочек по значению:

Даже если заголовок секции передается по значению, заголовок содержит указателя элементы массив, поэтому как исходный заголовок среза, так и копия заголовка, переданного функции, описывает один и тот же массив. Следовательно, когда функция возвращает, измененные элементы могут быть , видимые через исходную переменную среза.

С помощью append вы редактируете заголовок среза. И

Таким образом, если мы хотим, чтобы написать функцию, которая изменяет заголовок, мы должны вернуть его в качестве параметра результата

Или:

Другой способ иметь функцию изменения заголовок среза должен передать указатель на него.

У вас также есть путаница в использовании указателей. Смотрите spec:

Для операнда х типа Т, операций адреса & х генерирует указатель типа * T к й.

А:

Для операнда х типа указателя * T, указатель косвенность * х означает переменную типа Т, на которую указывает х.

Таким образом, ваш пример *stack = append(*stack, x) не означает, что вы передаете указатель на append, совсем наоборот - вы разыменования указателя передать значение он, указывая на.

2

Проблема, с которой вы сталкиваетесь, заключается в том, что append возвращает ссылку на новый фрагмент. Вы не можете изменить значение приемника функции, если это не указатель.

То, что я рекомендовал бы, а делает обертку-структуру:

type Stack struct{ 
    data []interface{} 
    size int 
} 
func (s *Stack) Push(i interface{}){ 
    s.data = append(s.data,i) 
    s.size++ 
} 
+1

Параметр 'struct' делает его легче понять, но' size' поле здесь совершенно лишними и требует лишней работы, чтобы сохранить ее, как вы можете просто получить его с помощью len (data) '. – icza

+0

Согласен. Это было главным образом продемонстрировать преимущество перед сырым срезом, поскольку вы можете добавить дополнительные данные по мере необходимости. – captncraig