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.)
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.
ausgezeichnete Antwort! :-) –
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
s/choice/trade/ – allyourcode