2013-10-07 7 views
5

Im folgenden Codefragment möchte ich verstehen, was genau in iPerson gespeichert wird, wenn der Inhalt noch nicht initialisiert ist: nur ein Wert von 0 Bytes? Oder ist es eigentlich ein Zeiger unter der Haube (und natürlich auch auf 0 Bytes initialisiert)? Was passiert genau unter iPerson = person?Wie genau sind Interface-Variablen in Go implementiert?

Wenn iPerson = person macht eine Kopie von person, was dann geschieht, wenn ein Objekt der Umsetzung IPerson aber mit einem anderen Größe/Speicher-Footprint zu iPerson zugewiesen wird? Ich verstehe, iPerson ist eine Variable auf dem Stapel gespeichert, so muss seine Größe festgelegt werden. Bedeutet das, dass der Heap tatsächlich unter der Haube verwendet wird, also wird iPerson tatsächlich als ein Zeiger implementiert, aber Zuweisungen kopieren immer noch das Objekt, wie durch den obigen Code demonstriert? Hier ist der Code:.

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 
} 

(Diese Frage ist das Ergebnis von mir zweite Gedanken über die genaue sachliche Richtigkeit meiner Antwort auf why runtime error on io.WriterString? mit Also habe ich einige Untersuchungen zu tun, um zu verstehen, um zu versuchen, entschieden, wie es ist genau das, Interface-Variablen und Zuweisungen, um sie in Go arbeiten)

EDIT:. nach ein paar nützliche Antworten erhalten zu haben, ich bin immer noch verwirrt mit diesem:

iPerson = person 
iPerson = &person 

-both sind legal. Für mich wirft dies jedoch die Frage auf, warum der Compiler eine solche schwache Typisierung erlaubt. Eine Folge der oben genannten ist dies:

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

während die erste Zeile zu ändern fixiert es:

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

... so ist es nicht möglich ist, statisch, um zu bestimmen, ob iPerson hält einen Zeiger oder einen Wert; und es scheint, dass irgendjemand ihm zur Laufzeit einen ohne Fehler zuweisen kann. Warum wurde eine solche Designentscheidung getroffen? Welchen Zweck erfüllt es? Es passt definitiv nicht in die "type safety" Denkweise.

Antwort

3

So sieht intern aus, die Schnittstellenvariable hält einen Zeiger auf, was ihm zugewiesen wurde. Ein Auszug aus http://research.swtch.com/interfaces:

Das zweite Wort in der Schnittstellenwert Punkte an den Istdaten, in diesem Fall eine Kopie b. Die Zuweisung var s Stringer = b macht eine Kopie von b statt Punkt b aus dem gleichen Grund, var c uint64 = b macht eine Kopie: wenn b später Änderungen, s und c sollen den ursprünglichen Wert haben, nicht die neue.

Meine Frage

[...], was dann geschieht, wenn ein Objekt der Umsetzung IPerson aber mit einer anderen Größe/Speicher-Footprint zu IPerson zugewiesen wird?

...Auch wird in dem Artikel beantwortet:

in Schnittstellen gespeicherten Werte können beliebig groß sein, aber nur ein Wort zu halten, den Wert in der Schnittstellenstruktur gewidmet ist, so ordnet die Zuordnung einen Teil des Speichers auf dem Heap und Aufzeichnungen der Zeiger in dem Einwortschlitz.

Also ja, eine Kopie auf dem Heap ist gemacht und ein Zeiger auf sie auf die Schnittstellenvariable zugewiesen. Aber für den Programmierer hat die Schnittstellenvariable offensichtlich die Semantik einer Wertvariablen und nicht eine Zeigervariable.

(Danke an Volker für die Bereitstellung des Links; aber auch der erste Teil seiner Antwort ist sachlich schlicht falsch ... Also ich weiß nicht, ob ich für die irreführende Information oder die Upvote für die nicht irreführende Downvote sollte und eher nützliche Verbindung (das auch seine eigene Antwort auf Widerspruch geschieht))

4

Wenn Sie die folgende Zeile ausführen.

iPerson = person 

Du einen Person Wert in dem Interface-Variable zu speichern. Da die Zuweisung an eine Struktur eine Kopie ausführt, nimmt Ihr Code eine Kopie an. Zum Abrufen der Struktur aus dem Inneren der Schnittstelle benötigen Sie eine weitere Kopie zu nehmen:

p := iPerson.(Person) 

so würde man nur selten das tun möchte, mit wandelbaren Typen. Wenn Sie stattdessen einen Zeiger auf die Struktur in der Schnittstelle Variablen speichern möchten, müssen Sie dies explizit tun:

iPerson = &person 

Soweit, was unter der Haube geht, sind Sie richtig, dass Interface-Variablen zuweisen Heap-Speicher zu Speichern Sie Werte, die größer als ein Zeiger sind, aber dies ist normalerweise für den Benutzer nicht sichtbar.

+0

Was mich verwirrt ist, warum Go sowohl iPerson = & Person als auch iPerson = Person erlaubt, ohne den Typ von iPerson ändern zu müssen. Es erlaubt Laufzeitfehler, die stattdessen statisch abgefangen werden könnten. Es ist etwas, was der Artikel, auf den Volker hingewiesen hat, nicht behandelt oder erwähnt. –

+0

Wenn Sie die Struktur als unveränderlichen Wert behandeln, kann es sinnvoll sein, sie nach Wert zu übergeben. In der Praxis werden Sie sie wahrscheinlich nicht verwechseln, wenn Sie Ihre Methoden so definieren, dass sie einen Zeigerempfänger verwenden, da diese Methoden nicht auf den Wert innerhalb des Schnittstellenwerts zugreifen können, so dass sie nicht verwechselt werden: http: // play .golang.org/p/E_8WjLS4S0 –

+0

OK, du sagst also, dass das in der Praxis kein Problem sein wird? Aber ich würde immer noch argumentieren, dass dies ein Mangel an Eleganz im Sprachdesign ist und das Typsystem könnte hier einen besseren Job machen. –

4

Sie fragen, warum beide

iPerson = person 
iPerson = &person 

erlaubt sind. Sie sind beide zulässig, da sowohl die Person als auch die Person die IPerson-Schnittstelle implementieren. Dies ist offensichtlich, da IPerson die leere Schnittstelle ist - jeder Wert implementiert sie.

Es ist wahr, dass Sie nicht statisch feststellen können, ob ein Wert von IPerson einen Zeiger oder einen Wert enthält. Na und? Alles, was Sie über IPerson wissen, ist, dass jedes Objekt, das in einem Wert dieses Typs gespeichert ist, die Liste der Methoden in der Schnittstelle implementiert. Die Annahme ist, dass diese Methoden korrekt implementiert sind. Ob IPerson einen Wert oder einen Zeiger enthält, spielt dabei keine Rolle.

Zum Beispiel, wenn die Methode etwas im Objekt gespeichert ändern soll, dann muss die Methode so ziemlich eine Zeigermethode sein, in diesem Fall kann nur ein Zeigerwert in der Variablen vom Schnittstellentyp gespeichert werden. Wenn jedoch keine der Methoden etwas ändert, das im Objekt gespeichert ist, können alle Wertmethoden sein, und ein Nicht-Zeigerwert kann in der Variablen gespeichert werden.

Verwandte Themen