In Go werden Strukturen (struct) standardmäßig nach Wert übergeben und zurückgegeben. Das bedeutet, dass beim Aufruf einer Funktion oder bei der Rückgabe aus dieser die gesamte Struktur kopiert wird. Für kleine Strukturen ist das transparent, aber bei großen ist die Frage kritisch.
Ursprünglich war Go auf eine effiziente Arbeit mit einer geringen Anzahl von Allokationen ausgerichtet. Das Risiko unbewusster Kopieroperationen großer Daten stellte sich ein, als Strukturen viele Felder und verschachtelte Objekte verwendeten. Die Leistung solcher Operationen kann leiden, und manchmal zeigt sich der Unterschied nur bei Profilierungen oder durch Probleme mit dem GC.
Wenn eine Struktur groß ist, ist das Kopieren bei jedem Funktionsaufruf, der Rückgabe oder Zuweisung kostspielig. Dies führt zu:
Für große Strukturen wird empfohlen, einen Zeiger auf die Struktur (*T) und nicht das Objekt selbst zu übergeben und zurückzugeben. Dies reduziert die Overheads und ermöglicht die Arbeit mit einer einzigen Instanz der Daten.
Beispielcode:
package main import "fmt" type Large struct { Data [1024]int } // Übergabe nach Wert (nicht korrekt für große Objekte) func ValueProcess(l Large) { l.Data[0] = 123 // ändert nur die Kopie } // Übergabe per Zeiger func PointerProcess(l *Large) { l.Data[0] = 456 // ändert das Original } func main() { a := Large{} ValueProcess(a) fmt.Println("Nach ValueProcess:", a.Data[0]) // 0 PointerProcess(&a) fmt.Println("Nach PointerProcess:", a.Data[0]) // 456 }
Wesentliche Merkmale:
1. Kann ein Zeiger auf eine lokale Struktur-Variable aus einer Funktion in Go zurückgegeben werden?
Ja. Go garantiert die Gültigkeit solcher Zeiger, indem es automatisch die Werte in den Heap verschiebt, auf die der Zeiger zurückgegeben wird (Escape to heap).
func NewLarge() *Large { l := Large{} return &l }
2. Wird das Original ändern, wenn eine Struktur nach Wert an die Funktion übergeben und die Felder darin geändert werden?
Nein: nur die Kopie wird geändert, und das Original bleibt außerhalb der Funktion unverändert.
3. Soll immer ein Zeiger für Strukturen verwendet werden?
Nein. Für kleine Strukturen (mit wenigen Feldern) ist die Übergabe nach Wert sicher und oft vorzuziehen (immutable/value-semantic), da sie Allokationen spart und die Belastung des GC reduziert.
In einem Logging-Service stellte jedes Ereignis eine große Struktur dar und wurde aus Funktionen nach Wert zurückgegeben — jede Änderung kopierte die Struktur vollständig.
Vorteile:
Nachteile:
Wir wechselten zu der Übergabe und Rückgabe von Strukturen per Zeiger, indem wir Daten über Signaturen wie func(l *Large) und func() *Large änderten.
Vorteile:
Nachteile: