2013-08-26 6 views
10

Scheint, wie Sie immer das wollen würden:Warum werden Empfänger in Go übergeben?

func (self *Widget) Do() { 
} 

anstelle diesen

func (self Widget) Do() { 
} 

Wenn ja, dann ist der Weg, um die frühere Semantik erhalten HABE unter Verwendung der letzteren Syntax sein. d.h. Empfänger sollten als Referenz übergeben werden.

Antwort

24

Es ist, weil everything in Go is pass by value. Dies macht es konsistent mit anderen Sprachen der C-Familie und bedeutet, dass Sie sich nie daran erinnern müssen, ob die Situation, die Sie betrachten, Wert ist oder nicht.

Von diesem Link:

Wie in allen Sprachen in der C-Familie, alles in Go wird als Wert übergeben. Das heißt, eine Funktion erhält immer eine Kopie des übergebenen Objekts, als ob es eine Zuweisungsanweisung gäbe, die dem Parameter den Wert zuweist. Wenn Sie beispielsweise einen int-Wert an eine Funktion übergeben, wird eine Kopie des int-Objekts erstellt. Wenn Sie einen Zeigerwert übergeben, wird eine Kopie des Zeigers erstellt, nicht jedoch die Daten, auf die er zeigt. (Siehe den nächsten Abschnitt für eine Diskussion darüber, wie diese Methode Empfänger auswirkt.)

Dann später:

func (s *MyStruct) pointerMethod() { } // method on pointer 
    func (s MyStruct) valueMethod() { } // method on value 

Für Programmierer ungewohnt Zeiger, die Unterscheidung zwischen diesen beiden Beispielen kann verwirrend sein, aber die Situation ist eigentlich sehr einfach. Wenn Sie eine Methode für einen Typ definieren, verhält sich der Empfänger (s in den obigen Beispielen) genau so, als wäre er ein Argument für die Methode. Ob der Empfänger als Wert oder als Zeiger definiert wird, ist dieselbe Frage, also ob ein Funktionsargument ein Wert oder ein Zeiger sein soll. Es gibt mehrere Überlegungen.

Erstens, und am wichtigsten, muss die Methode den Empfänger ändern? Wenn dies der Fall ist, muss der Empfänger ein Zeiger sein. (Slices und Maps fungieren als Referenzen, also ist ihre Geschichte etwas subtiler, aber um zum Beispiel die Länge eines Slices in einer Methode zu ändern, muss der Empfänger immer noch ein Pointer sein.) Wenn in den obigen Beispielen pointerMethod die Felder von ändert s, der Aufrufer wird diese Änderungen sehen, aber ValueMethod wird mit einer Kopie des Arguments des Aufrufers aufgerufen (das ist die Definition der Übergabe eines Wertes), so dass Änderungen für den Aufrufer unsichtbar gemacht werden.

Zeiger Empfänger sind übrigens identisch mit der Situation in Java, obwohl in Java die Zeiger unter den Deckeln versteckt sind; es ist Gos Wertempfängern, die ungewöhnlich sind.

Zweitens ist die Berücksichtigung der Effizienz. Wenn der Empfänger groß ist, beispielsweise eine große Struktur, wird es viel billiger sein, einen Zeigerempfänger zu verwenden.

Weiter ist die Konsistenz. Wenn einige der Methoden des Typs Zeigerempfänger haben müssen, sollte der Rest auch, also die Methodenmenge konsistent sein, unabhängig davon, wie der Typ verwendet wird. Einzelheiten finden Sie im Abschnitt über Methodensätze.

Für Typen wie Basistypen, Slices und kleine Strukturen ist ein Wertempfänger sehr billig. Wenn also die Semantik der Methode keinen Zeiger erfordert, ist ein Wertempfänger effizient und klar.

+1

ausgezeichnete Antwort! :-) –

+0

Alles im zweiten Zitat suggeriert mir, dass in einem guten Design fast jeder Empfänger ein Zeiger sein sollte. Der einzige Fall, in dem erwähnt wird, wo Sie die Verwendung von Nicht-Pointer in Betracht ziehen könnten, ist, wenn der Wert klein ist. Verallgemeinern um des kleinen Wertes willen, auf Kosten des Entmutigens guten Designs, scheint keine gute Wahl zu sein. – allyourcode

+0

s/choice/trade/ – allyourcode

6

Manchmal Sie nicht wollen aber durch Verweis übergeben. Die Semantik von

func (self Widget) Get() Value { 
} 

Kann nützlich sein, wenn Sie zum Beispiel ein kleines unveränderliches Objekt haben. Der Anrufer kann sicher wissen, dass diese Methode seinen Empfänger nicht ändert. Sie können dies nicht wissen, wenn der Empfänger ein Zeiger ist, ohne den Code zuerst zu lesen.

auf, dass für

Instanz zu erweitern
// accessor for things Config 
func (self Thing) GetConfig() *Config { 
} 

Nur durch bei dieser Methode suchen Ich weiß, kann GetConfig ist immer die gleiche Config zurück gehen. Ich kann diese Konfiguration ändern, aber ich kann den Zeiger nicht auf Config in Thing ändern. Es ist ziemlich nah an einem const Zeiger in Thing.

+0

Deshalb sollte Go const haben: P – allyourcode

+0

Sie wissen nicht, dass GetConfig die gleiche Konfiguration zurückgibt; Das ist nur dann der Fall, wenn nicht versucht wird, auf den globalen Zustand zuzugreifen. der Zufallszahlengenerator des Systems. – allyourcode

+0

Kopieren sollte nicht als Verteidigung gegen Änderungen verwendet werden. Es funktioniert, aber es hat erhebliche (versteckte) Leistungskosten. – allyourcode

2

Scheint, wie Sie immer das wollen würde:

Nr Der Wert Empfänger allgemeiner ist. Es kann an allen Stellen verwendet werden, an denen ein Zeigerempfänger sein kann; aber ein Zeigerempfänger kann nicht an allen Stellen verwendet werden, an denen ein Wertempfänger stehen kann - zum Beispiel, wenn Sie einen rvalue-Ausdruck des Typs Widget; Sie können Wertempfängermethoden, aber keine Zeigerempfängermethoden aufrufen.

+0

Das scheint wie ein Anti-Muster. Scheint, dass deine Ausdrücke vom Typ * Widget sein sollten. Andernfalls werden Sie wahrscheinlich unwissentlich viele Daten kopieren. – allyourcode

+0

Sie können einen Wertempfänger nicht verwenden, wenn Sie das Objekt selbst ändern möchten, richtig? Sie können also nicht immer einen Wertempfänger verwenden. – korylprince

+0

@korylprince: Das ist nicht, was ich mit "kann verwendet werden" gemeint. Was ich meinte, ist, dass, wenn Sie eine Methode mit einem Wertempfänger und eine Methode mit einem Zeigerempfänger haben, es einige Ausdruckskontexte gibt, in denen Sie die Methode mit Zeigerempfänger nicht verwenden können (wie es nicht kompiliert), aber wo Sie verwenden können die Methode mit Wertempfänger. Natürlich können gewisse Dinge nicht mit Methoden mit Wertempfängern gemacht werden. Aber darum geht es nicht. Die Frage fragt nach einer hypothetischen Wert-Empfänger-Methode und fragt, ob sie immer Zeiger-Empfänger sein soll. – newacct

Verwandte Themen