2013-10-07 10 views
5

Nello snippet di codice seguente, mi piacerebbe capire cosa viene memorizzato esattamente in iPerson quando il suo contenuto non è ancora inizializzato: solo un valore di 0-byte? O è in realtà un puntatore sotto il cofano (e anche inizializzato a 0-bytes ovviamente)? In ogni caso, cosa succede esattamente a iPerson = person?In che modo vengono implementate le variabili dell'interfaccia in Go?

Se iPerson = person esegue una copia di person, cosa succede quando un oggetto che implementa IPerson ma con un diverso footprint formato/memoria viene assegnato ad iPerson? Capisco iPerson è una variabile memorizzata nello stack, quindi la sua dimensione deve essere corretta. Ciò significa che l'heap è effettivamente utilizzato sotto il cofano, quindi iPerson viene effettivamente implementato come puntatore, ma i compiti continuano a copiare l'oggetto, come dimostrato dal codice precedente? Ecco il codice:.

type Person struct{ name string } 

type IPerson interface{} 

func main() { 
    var person Person = Person{"John"} 
    var iPerson IPerson 
    fmt.Println(person) // => John 
    fmt.Println(iPerson) // => <nil> ...so looks like a pointer 

    iPerson = person  //   ...this seems to be making a copy 
    fmt.Println(iPerson) // => John 

    person.name = "Mike" 
    fmt.Println(person) // => Mike 
    fmt.Println(iPerson) // => John ...so looks like it wasn't a pointer, 
         //   or at least something was definitely copied 
} 

(Questa domanda è il risultato di me avere ripensamenti sulla precisa di fatto correttezza della mia risposta a why runtime error on io.WriterString? Così ho deciso di provare a fare qualche indagine per capire come è esattamente questo variabili dell'interfaccia e le assegnazioni per farli lavorare in Go)

EDIT:. dopo aver ricevuto alcune risposte utili, io sono ancora perplesso con questo:

iPerson = person 
iPerson = &person 

- entrambi sono legali. Tuttavia, a mio avviso, ciò solleva la questione del perché il compilatore permetta di effettuare una digitazione così debole? Una conseguenza di quanto sopra è questo:

iPerson = &person 
var person2 = iPerson.(Person) # panic: interface conversion: interface is *main.Person, not main.Person 

che modifica la prima riga ripara:

iPerson = person 
var person2 = iPerson.(Person) # OK 

... quindi non è possibile determinare se staticamente iPerson contiene un puntatore o un valore; e sembra che nulla possa assegnarne uno in fase di esecuzione senza errori. Perché è stata presa una tale decisione di progettazione? Quale scopo serve? Sicuramente non si adatta alla mentalità del "tipo sicurezza".

risposta

3

Quindi, sembra internamente, la variabile di interfaccia contiene un puntatore a ciò che gli è stato assegnato. Un estratto da http://research.swtch.com/interfaces:

La seconda parola del valore interfaccia punti ai dati effettivi, in questo caso una copia dib. L'assegnazione var s Stringer = b fa una copia di b anziché il punto b per lo stesso motivo per cui var c uint64 = b fa una copia: se b modifiche successive, s e c si suppone che abbiano il valore originale, non quello nuovo.

La mia domanda

[...] cosa succede allora quando un oggetto che implementa IPerson ma con una diversa impronta di dimensioni/memoria viene assegnato a IPerson?

...inoltre viene risposto nell'articolo:

valori memorizzati nelle interfacce potrebbero essere arbitrariamente grande, ma solo una parola è dedicata a tenere il valore nella struttura dell'interfaccia, così l'assegnazione alloca un blocco di memoria sul mucchio e record il puntatore nello slot di una parola.

Quindi sì, una copia sull'heap viene creata e un puntatore ad esso assegnato alla variabile di interfaccia. Ma, apparentemente, per il programmatore, la variabile di interfaccia ha la semantica di una variabile di valore, non una variabile di puntatore.

(Grazie a Volker per aver fornito il collegamento, ma anche, la prima parte della sua risposta è in realtà semplicemente sbagliata ... Quindi non so se dovrei downvotare per le informazioni fuorvianti o upvote per il non-fuorviante e il collegamento piuttosto utile (che avviene anche in contraddizione con la sua risposta))

4

Quando si esegue la seguente riga:.

iPerson = person 

si memorizza un valore Person nella variabile di interfaccia. Poiché l'assegnazione a una struttura esegue una copia, sì il tuo codice sta prendendo una copia. Per recuperare lo struct da dentro l'interfaccia è necessario prendere un'altra copia:

p := iPerson.(Person) 

in modo che ci raramente vuole fare questo con i tipi mutabili. Se invece desidera memorizzare un puntatore alla struct nella variabile di interfaccia, è necessario fare questo in modo esplicito:

iPerson = &person 

Per quanto riguarda ciò che accade sotto il cofano, hai ragione che le variabili dell'interfaccia di allocare lo spazio heap memorizza valori più grandi di un puntatore, ma di solito non è visibile all'utente.

+0

Quali sono gli enigmi per cui Go consente sia 'iPerson = & person' che' iPerson = person' senza dover cambiare il tipo di 'iPerson'. Consente errori di tipo runtime che potrebbero invece essere rilevati staticamente. È qualcosa che l'articolo segnalato da Volker non copre o menziona. –

+0

Se si considera la struttura come un valore immutabile, potrebbe essere molto sensato passarla per valore. In pratica, probabilmente non li mescolerai se definisci i tuoi metodi per prendere un ricevitore puntatore, poiché quei metodi non saranno in grado di accedere al valore contenuto nel valore dell'interfaccia, quindi non saranno confusi: http: // play .golang.org/p/E_8WjLS4S0 –

+0

OK, quindi stai dicendo che in pratica questo non sarà un problema? Ma continuerei a sostenere che questa è una ineleganza nella progettazione della lingua e che il sistema dei tipi potrebbe fare un lavoro migliore qui. –

4

chiedete perché sono permessi entrambi

iPerson = person 
iPerson = &person 

. Sono entrambi consentiti perché sia ​​la persona che la persona & implementano l'interfaccia IPerson. Questo è ovvio, perché IPerson è l'interfaccia vuota - ogni valore lo implementa.

È vero che non è possibile determinare staticamente se un valore di IPerson contiene un puntatore o un valore. E allora? Tutto ciò che sai su IPerson è che qualsiasi oggetto memorizzato in un valore di quel tipo implementa l'elenco di metodi nell'interfaccia. L'ipotesi è che quei metodi siano implementati correttamente. Se IPerson detiene un valore o un puntatore è irrilevante.

Ad esempio, se il metodo deve modificare qualcosa memorizzato nell'oggetto, allora il metodo deve essere praticamente un metodo puntatore, nel qual caso è possibile memorizzare solo un valore puntatore nella variabile del tipo di interfaccia. Ma se nessuno dei metodi modifica qualcosa memorizzato nell'oggetto, allora possono essere tutti metodi di valore e un valore non puntatore può essere memorizzato nella variabile.