In Go können Methoden sowohl für Werte als auch für Zeiger auf den Typ (Value/Pointer Receiver) deklariert werden. Diese Besonderheit wurde seit den frühen Versionen der Sprache beibehalten, um klar zu kontrollieren, wer die Ursprungsdaten ändern kann. Ein klassisches Problem ist die Notwendigkeit, zwischen den Semantiken von Value (Kopie, ändert nicht) und Pointer (gemeinsamer Zugriff auf Daten und Möglichkeit zur Modifikation) zu unterscheiden.
Problem — Es ist leicht, einen Fehler zu machen, indem man eine Methode mit einem Value Receiver deklariert und nicht den erwarteten Effekt erhält, oder eine Value-Methode auf einer Pointer-Variable aufruft.
Lösung — Halte dich an die folgenden Regeln:
Beispielcode:
type Counter struct { Value int } func (c Counter) IncCopy() { c.Value++ } // Value Receiver func (c *Counter) IncPointer() { c.Value++ } // Pointer Receiver c := Counter{} c.IncCopy() // Value bleibt 0 c.IncPointer() // Value wird 1
Wichtige Merkmale:
Kann man eine Value Receiver-Methode auf einem Zeiger aufrufen, und eine Pointer-Methode auf einem Wert?
Go "unter der Haube" dereferenziert automatisch Zeiger oder nimmt ihre Adressen, daher ist der Aufruf erlaubt, wenn die Typen kompatibel sind. Aber nicht immer — bei Interfaces funktioniert das nicht immer so vorhersehbar.
var c Counter (&c).IncCopy() // Value-Methode kann über einen Zeiger aufgerufen werden c.IncPointer() // Pointer-Methode kann aufgerufen werden, Go nimmt automatisch die Adresse
Was passiert, wenn eine Struktur nur Pointer-Methoden implementiert, aber als Wert in ein Interface übergeben wird?
Ein solches Objekt implementiert das Interface nicht, wenn es Pointer-Methoden erfordert, daher kann es zu einem Panic oder einem Kompilierungsfehler kommen.
type D interface { IncPointer() } func f(d D) {} c := Counter{} f(c) // Fehler! Counter implementiert das Interface nicht als Wert f(&c) // korrekt
Wird sich die Struktur beim Aufruf einer Methode des Pointer Receivers ändern, wenn eine Kopie des Zeigers übergeben wird?
Ja, auch wenn der Zeiger kopiert wird, liegt darunter dasselbe Objekt — das Ergebnis wird gleich sein.
c := Counter{} p := &c p2 := p p2.IncPointer() // Value erhöht sich
Ein Ingenieur implementiert eine Struktur mit Value Receiver-Methoden "Update". Die Struktur wird über ein Interface übergeben, aber Änderungen „verschwinden“ — da sie mit einer Kopie arbeiten.
Vorteile:
Nachteile:
Eindeutige Vereinbarung im Team: Alle Methoden, die den Zustand ändern, sind nur Pointer Receiver, Interfaces werden nur durch Zeiger implementiert, Value — für „Erweiterungen“ und Hilfsfunktionen.
Vorteile:
Nachteile: