Slices en arrays zijn een van de meest gebruikte gegevensstructuren in Go. Ondanks de vergelijkbare syntaxis kan het verschil in hun structuur en gedrag leiden tot fouten in prestatie, geheugen en semantiek.
Achtergrond:
Go heeft vanaf het begin een expliciet geheugenbeheer model gekozen, waarbij arrays (arrays) een reeks elementen met een vaste grootte zijn, terwijl slices (slices) een dynamisch uitzicht op een array zijn. Deze scheiding maakt het mogelijk om de kosten van operaties en het gedrag van de code te beheersen.
Probleem:
De belangrijkste moeilijkheid is de verwarring tussen het kopiëren van een array (value semantics) en de "verwijzing" van een slice. Fouten komen vaak voor bij het doorgeven van deze types aan functies en het wijzigen van waarden, wat leidt tot onvoorziene bijwerkingen.
Oplossing:
Arrays worden altijd gekopieerd bij doorgeven per waarde: de functie krijgt een kopie van de hele inhoud. Een slice daarentegen is een kleine structuur (header) die een pointer naar de array, de lengte en de capaciteit bevat. Wijzigingen binnen de slice zijn zichtbaar van buitenaf, als de inhoud van de array wordt gewijzigd (maar niet als de slice zelf naar een nieuwe array wordt omgeleid binnen de functie).
Voorbeeldcode:
func updateArray(arr [3]int) { arr[0] = 10 } func updateSlice(slc []int) { slc[0] = 10 } func main() { a := [3]int{1,2,3} b := []int{1,2,3} updateArray(a) updateSlice(b) fmt.Println(a) // [1 2 3] fmt.Println(b) // [10 2 3] }
Belangrijke kenmerken:
Wat gebeurt er als je de lengte van een slice in een functie wijzigt? Heeft dit invloed op de oorspronkelijke slice?
Nee, het wijzigen van de lengte van een slice (bijvoorbeeld met slc = slc[:2]) binnen de functie betreft alleen de lokale kopie van de header. De oorspronkelijke slice blijft ongewijzigd.
Geeft de append operator de gewijzigde slice in hetzelfde geheugengebied terug?
Niet noodzakelijk. Als de capaciteit niet voldoende is, wordt er een nieuwe array aangemaakt en wordt de pointer naar de nieuwe array teruggegeven. De oude array blijft onaangeroerd.
Voorbeeldcode:
s := []int{1,2,3} s2 := append(s, 4, 5, 6) // s2 kan in een nieuw geheugengebied zijn
Kun je een array toewijzen aan een slice of omgekeerd?
Nee. []int en [5]int zijn verschillende types. Om een array als slice door te geven, moet je gebruik maken van conversie arr[:]. Het omgekeerde is niet mogelijk.
Een junior ontwikkelaar implementeerde een functie om een array bij te werken, door de array aan de functie door te geven in de veronderstelling dat wijzigingen zouden worden toegepast op de oorspronkelijke array. De aanpassingen werden niet "opgeslagen".
Voordelen:
Nadelen:
De functie accepteerde een slice en retourneerde expliciet een gewijzigde kopie, waardoor de voorspelbaarheid van het effect toenam. Alle wijzigingen waren opzettelijk, gegevens "lekten" niet en werden niet impliciet gewijzigd.
Voordelen: