2013-08-06 22 views
5

Diciamo che ho una semplice struct una con una proprietà di stringa b:Mappa di struct vs array di struct in Go

type A struct { 
    B string 
} 

Il seguente codice che utilizza una matrice di una tipi:

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

Stamperà "test1" come previsto.

Ma questo codice che sembra altrettanto semplice:

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

Will non stampare "test2", ma invece comporterà l'errore seguente:

cannot assign to testMap["key"].B

Quindi, Perché l'assegnazione al la sottoproprietà in una mappa genera un errore durante l'assegnazione alla sottoproprietà in un lavoro array come previsto? Voglio sapere sia perché questo non funziona per le mappe E perché funziona per gli array. Mi piacerebbe anche qualche speculazione sul perché hanno progettato il linguaggio con questa differenza tra le due strutture dati.

+0

'testArray' non è un" array ". È una "fetta". "Array" è qualcosa di diverso. – newacct

risposta

10

Ho risposto a lungo sulla mailing list, ma la breve spiegazione è che questo non funziona perché le voci della mappa non sono indirizzabili. Ciò significa che non puoi prendere l'indirizzo di una voce in una mappa. Questo perché aggiungere un nuovo valore a una mappa può far spostare le voci della mappa, in modo che cambino gli indirizzi. Poiché non puoi prendere l'indirizzo di una voce in una mappa, tutte le operazioni della mappa usano valori interi: copia un intero valore da una mappa, aggiungi un intero a una mappa. Assegnare a un campo di una struttura in una mappa richiederebbe un'operazione di lettura-modifica-scrittura, che le mappe non supportano (potrebbero, ma non lo fanno, e c'è un costo per supportarle).

Gli elementi in matrici e sezioni sono indirizzabili perché non si spostano dopo che sono stati creati.

+1

Questo ha risposto alla mia domanda. Non è "solo perché è così che è stato progettato" o "magico" o qualcosa del genere, la ragione per cui gli array lo consentono e le mappe non hanno senso. Grazie! – GreenMachine

+0

Inoltre, la tua risposta approfondita è ancora più utile. Potrebbe valere la pena postare tutto qui ma per ora, ecco un link ad esso: https://groups.google.com/d/msg/golang-nuts/FCcLsuWsF_U/qk6SLNcHvJIJ – GreenMachine

2

L'elemento di matrice è un lvalue. Con le mappe è un po 'più complesso. In:

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

LHS m[T(expr)]è un lvalue. Tuttavia, in:

type U struct{ 
     F V 
} 

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

LHS m[T(expr)].F non è un lvalue più. La prima parte, m[T(expr)] valuta un'istanza di tipo U. Quell'istanza è "fluttuante", non ha più una casa. Assegnare qualcosa ai suoi campi è sicuramente un errore, quindi il compilatore urla.

E 'più o meno lo stesso come differenza tra:

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

Top risolvere il problema, è possibile utilizzare puntatore ad una struct:

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

La mappa produce prima un puntatore a U che viene poi utilizzato per impostare il campo.

1

Tecnicamente, in base al riferimento del linguaggio, l'espressione testmap["key"].B non è addressable, quindi non può essere utilizzata come lato sinistro di assignment.

Quindi la domanda potrebbe dover essere spostata su: perché quell'espressione non è indirizzabile? Non ne sono ancora abbastanza sicuro ...

... ah. È perché testmap["key"] ci sta restituendo una copia della struttura. Mutare quella copia probabilmente non è ciò che vogliamo fare, dal momento che non influenzerà la struttura originale nella mappa.

4

Il problema è che nell'esempio della mappa, testMap["key"] restituisce un letterale, non un puntatore. Ciò significa che modificarlo non ha senso, quindi il compilatore non lo consente.E 'fondamentalmente equivalente a:

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

... e poi non utilizza mai v nuovo. Non ha alcun effetto. È equivalente a non eseguire mai quelle due linee in primo luogo. Ecco perché il compilatore non ti lascerà fare. D'altra parte, se avessi fatto una mappa di puntatori in A, saresti in affari. Questo sarebbe compilazione:

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

La ragione per cui questo funziona è che dereferencing e assegnando a un valore di puntatore fa avere un effetto.

+0

Mi piace questa spiegazione per le mappe, ma cosa c'è di diverso negli array? Anche gli array non restituiscono i puntatori, quindi perché funziona nell'esempio dell'array? – GreenMachine

+0

... magia, penso. – joshlf

+0

Penso che sia solo il modo in cui è progettato il linguaggio. Gli array sono magici. Potrebbero teoricamente farlo anche con le mappe, ma non lo erano per qualche ragione, suppongo. – joshlf