In Go worden structuren (struct) standaard per waarde doorgegeven en teruggegeven. Dit betekent dat er een kopie van de gehele structuur plaatsvindt bij het aanroepen van een functie of bij het retourneren ervan. Voor kleine structuren is dit transparant, maar voor grote structuren is dit een kritische kwestie.
Aanvankelijk was Go gericht op efficiënte werking met een klein aantal allocaties. Echter, het risico van onbewuste kopieën van grote gegevens ontstond toen structuren meerdere velden en geneste objecten gebruikten. De prestaties van zulke operaties kunnen lijden, en soms komt het verschil pas aan het licht tijdens profielanalyses of door problemen met de GC.
Als een structuur groot is, dan blijkt het kopiëren bij elke functie-aanroep, terugkeer of toewijzing kostbaar te zijn. Dit leidt tot:
Voor grote structuren wordt aanbevolen om een pointer naar de structuur (*T) door te geven en terug te geven in plaats van het object zelf. Dit vermindert de overhead en zorgt ervoor dat er met één exemplaar van de gegevens wordt gewerkt.
Voorbeeldcode:
package main import "fmt" type Large struct { Data [1024]int } // Doorgeven per waarde (niet correct voor grote objecten) func ValueProcess(l Large) { l.Data[0] = 123 // verandert alleen de kopie } // Doorgeven per pointer func PointerProcess(l *Large) { l.Data[0] = 456 // verandert het origineel } func main() { a := Large{} ValueProcess(a) fmt.Println("Na ValueProcess:", a.Data[0]) // 0 PointerProcess(&a) fmt.Println("Na PointerProcess:", a.Data[0]) // 456 }
Belangrijke kenmerken:
1. Is het mogelijk om een pointer naar een lokale variabele van een structuur vanuit een functie in Go terug te geven?
Ja. Go garandeert de geldigheid van zulke pointers door automatisch de waarden naar de heap te verplaatsen waar de pointer naar teruggegeven wordt (escape to heap).
func NewLarge() *Large { l := Large{} return &l }
2. Zult u het origineel wijzigen als u een structuur per waarde naar de functie doorgeeft en de velden van binnenin verandert?
Nee: alleen de kopie verandert, het origineel buiten de functie blijft hetzelfde.
3. Moet u altijd pointers voor structuren gebruiken?
Nee. Voor kleine (weinig velden) structuren is doorgeven per waarde veilig en vaak beter (immutable/value-semantic), wat allocaties bespaart en de belasting op de GC verlaagt.
In de loggingdienst was elk evenement een grote structuur en werd deze per waarde uit functies teruggegeven — elke wijziging kopieerde de structuur volledig.
Voordelen:
Nadelen:
We zijn overgestapt op het doorgeven en teruggeven van structuren per pointer, waarbij we gegevens wijzigden via de handtekeningen func(l *Large) en func() *Large.
Voordelen:
Nadelen: