2013-08-06 15 views
5

Sagen wir, ich habe eine einfache Struktur ein mit einem String-Eigenschaft b:Karte von structs vs Array von Strukturen in Go

type A struct { 
    B string 
} 

Der folgende Code eine Reihe von A-Typen verwenden:

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

Wird "test1" wie erwartet ausgeben.

Aber dieser Code, der ebenso einfach scheint:

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

Wird nicht drucken "test2", sondern führt in dem folgenden Fehler:

cannot assign to testMap["key"].B

So warum die sich auf die Zuweisung von Untereigenschaft in einer Zuordnung führen zu einem Fehler beim Zuweisen der Untereigenschaft in einem Array wie erwartet? Ich möchte wissen, warum dies nicht funktioniert für Karten und warum es für Arrays funktioniert. Ich würde auch gerne darüber spekulieren, warum sie die Sprache mit diesem Unterschied zwischen den beiden Datenstrukturen entworfen haben.

+0

'testArray' ist kein" Array ". Es ist eine "Scheibe". "Array" ist etwas anderes. – newacct

Antwort

10

Ich antwortete ausführlich auf der Mailing-Liste, aber die kurze Erklärung ist, dass dies nicht funktioniert, weil Karteneinträge nicht adressierbar sind. Das bedeutet, dass Sie die Adresse eines Eintrags in einer Karte nicht übernehmen können. Dies liegt daran, dass das Hinzufügen eines neuen Werts zu einer Karte dazu führen kann, dass sich die Karteneinträge verschieben, sodass sich die Adressen ändern. Da Sie die Adresse eines Eintrags in einer Map nicht verwenden können, verwenden alle Map-Operationen ganze Werte: Kopieren Sie einen ganzen Wert aus einer Map, fügen Sie ein Ganzes zu einer Map hinzu. Die Zuordnung zu einem Feld einer Struktur in einer Map würde einen Read-Modify-Write-Vorgang erfordern, den die Maps nicht unterstützen (sie könnten dies, aber nicht, und ihre Unterstützung kostet sie).

Elemente in Arrays und Slices sind adressierbar, da sie sich nach der Erstellung nicht bewegen.

+1

Dies beantwortet meine Frage. Es ist nicht "nur weil das ist, wie es entworfen wurde" oder "Magie" oder so, der Grund dafür, warum Arrays dies erlauben, und Karten machen keinen Sinn. Vielen Dank! – GreenMachine

+0

Auch Ihre eingehende Antwort ist noch hilfreicher. Es lohnt sich vielleicht, dies hier zu veröffentlichen, aber für den Moment ist hier ein Link dazu: https://groups.google.com/d/msg/golang-nuts/FCcLsuWsF_U/qk6SLNcHvJIJ – GreenMachine

2

Array-Element ist ein lvalue. Mit Karten ist es ein bisschen komplexer. In:

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

der LHS m[T(expr)]ist ein L-Wert. Doch in:

type U struct{ 
     F V 
} 

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

der LHS m[T(expr)].F ist kein L-Wert mehr. Der erste Teil m[T(expr)] wird zu einer Instanz des Typs U ausgewertet. Diese Instanz "schwebt", sie hat kein Zuhause mehr. Es ist sicherlich ein Fehler, seinen Feldern etwas zuzuweisen, also schreit der Compiler.

Es ist mehr oder weniger die gleiche wie die Differenz zwischen:

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

Top, das Problem beheben, können Sie Zeiger auf eine Struktur verwenden:

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

Die Karte zuerst einen Zeiger auf U ergibt mit dem dann das Feld gesetzt wird.

1

Technisch entspricht der Ausdruck testmap["key"].B laut der Sprachreferenz nicht addressable, daher kann er nicht als linke Seite einer assignment verwendet werden.

Also muss die Frage vielleicht verschoben werden: Warum ist dieser Ausdruck nicht adressierbar? Ich bin mir da noch nicht ganz sicher ...

... ah. Es ist, weil testmap["key"] uns eine Kopie der Struktur zurück gibt. Das mutieren dieser Kopie ist wahrscheinlich nicht das, was wir tun wollen, da es die ursprüngliche Struktur in der Karte nicht beeinflusst.

4

Das Problem besteht darin, dass im Beispiel testMap["key"] ein Literal zurückgegeben wird, kein Zeiger. Dies bedeutet, dass es nicht sinnvoll ist, sie zu ändern, so dass der Compiler es nicht erlaubt.Es ist im Grunde gleichwertig:

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

... und dann nie v wieder verwenden. Es hat keine Wirkung. Es ist gleichbedeutend, diese beiden Zeilen überhaupt nie auszuführen. Deshalb lässt der Compiler das nicht zu. Auf der anderen Seite, hätten Sie eine Karte von Zeigern zu A gemacht, wären Sie im Geschäft. Diese würde kompilieren:

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

Der Grund, dass dies funktioniert, ist, dass dereferencing und auf einen Zeiger-Wert zuweisen tut eine Wirkung haben.

+0

Ich mag diese Erklärung für Karten, aber was ist anders an Arrays? Arrays geben auch keine Zeiger zurück. Warum funktioniert das im Array-Beispiel? – GreenMachine

+0

... Magie, denke ich. – joshlf

+0

Ich denke, es ist genau so, wie die Sprache entworfen wurde. Arrays sind magisch. Sie könnten es theoretisch auch mit Karten machen, aber aus irgendeinem Grund nicht, nehme ich an. – joshlf