2013-08-06 2 views
5

Допустим, у меня есть простой структуры а, с строкой свойства Ь:Карта структур против массива структур в Go

type A struct { 
    B string 
} 

Следующий код, используя массив A типов:

testArray := []A{A{}} 
testArray[0].B = "test1" 
fmt.Println(testArray[0].B) 

Распечатает «test1», как ожидалось.

Но этот код, который кажется столь же прост:

testMap := make(map[string]A) 
testMap["key"] = A{} 
testMap["key"].B = "test2" 
fmt.Println(testMap["key"].B) 

Будет не печатает «test2», но вместо этого приведет к следующей ошибке:

cannot assign to testMap["key"].B

Итак, почему назначение на subproperty в карте приводит к ошибке, а присвоение подзадачи в массиве работает как ожидалось? Я хочу знать, почему это не работает для карт и почему это работает для массивов. Мне также нравятся некоторые предположения о том, почему они разработали язык с этой разницей между двумя структурами данных.

+0

'testArray' не является« массивом ». Это «срез». «Массив» - это нечто другое. – newacct

ответ

10

Я немного ответил на список рассылки, но краткое объяснение состоит в том, что это не работает, потому что записи в карте не адресуются. Это означает, что вы не можете взять адрес записи на карте. И это связано с тем, что добавление нового значения в карту может привести к сдвигу записей в карте, чтобы эти адреса изменились. Поскольку вы не можете взять адрес записи на карте, все операции с картами используют целые значения: скопируйте целое значение из карты, добавьте целое к карте. Присвоение одному полю структуры на карте потребует операции чтения-изменения-записи, которые не поддерживают (они могут, но они этого не делают, и есть затраты на их поддержку).

Элементы в массивах и срезах адресуемы, потому что они не перемещаются после их создания.

+1

Это ответило на мой вопрос. Его не «просто потому, что так оно и было спроектировано» или «волшебство» или что-то еще, причина того, почему массивы позволяют это, и карты не имеют смысла. Спасибо! – GreenMachine

+0

Кроме того, вы более углубленный ответ еще более полезен. Возможно, стоит разместить все здесь, но на данный момент ссылка на него: https://groups.google.com/d/msg/golang-nuts/FCcLsuWsF_U/qk6SLNcHvJIJ – GreenMachine

2

Элемент массива - lvalue. С картами это немного сложнее. В:

m := map[T]U{} 
m[T(expr)] = U(expr) 

и LHS m[T(expr)]является именующим. Однако в:

type U struct{ 
     F V 
} 

m := map[T]U{} 
m[T(expr)].F = 34 

и LHS m[T(expr)].F не именующий больше. Первая часть, m[T(expr)] оценивает экземпляр типа U. Этот экземпляр «плавающий», у него больше нет дома. Присвоение чего-то своим полям, безусловно, является ошибкой, поэтому компилятор кричит.

Это более или менее такой же, как разница между:

var v U 
v.F = 42 // ok 
U{}.F = 42 // not ok 

Top решить эту проблему, вы можете использовать указатель на структуру:

m := map[T]*U{} 
m[T(expr)].F = 42 

Карта первая дает указатель на U который затем используется для установки поля.

1

Технически, в соответствии с ссылкой на язык выражение testmap["key"].B не является addressable, поэтому его нельзя использовать как левую сторону assignment.

Так что, возможно, вопрос должен быть перенесен на: почему это выражение не адресуется? Я еще не совсем уверен ...

... ах. Это потому, что testmap["key"] возвращает нам копию структуры. Мутирование этой копии, вероятно, не является тем, что мы хотим сделать, поскольку это не повлияет на исходную структуру на карте.

4

Проблема в том, что на примере карты testMap["key"] возвращает литерал, а не указатель. Это означает, что его изменение не имеет смысла, поэтому компилятор запрещает его.Это в основном эквивалентно:

v := testMap["key"] 
v.B = "test2" 

... и тогда никогда не используя v снова. Это не имеет никакого эффекта. Это эквивалентно тому, чтобы никогда не выполнять эти две строки в первую очередь. Вот почему компилятор не позволит вам это сделать. С другой стороны, если бы вы сделали карту указателей в A, вы были бы в бизнесе. Это бы компилировать:

testMap := make(map[string]*A) 
testMap["key"] = &A{} 
testMap["key"].B = "test2" 

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

+0

Мне нравится это объяснение для карт, но что отличаетс от массивов? Массивы также не возвращают указатели, так почему это работает в примере массива? – GreenMachine

+0

... магия, думаю. – joshlf

+0

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

Смежные вопросы