In Go zijn anonieme functies (function literals, closures) geïntroduceerd ter ondersteuning van functionele stijl, callbacks en beknopte inkapseling van algoritmen. Ze worden vaak gebruikt voor het verwerken van collecties, asynchrone taken en als parameters doorgegeven.
Zonder anonieme functies wordt de code overmatig: elke verwerking moet in een aparte benoembare functie worden geplaatst. Maar er rijzen vragen: hoe werkt de "vastlegging" van variabelen, waar wordt het geheugen opgeslagen, wat zijn de bijzonderheden bij het declareren en doorgeven als argumenten? Is de vastlegging van variabelen beschermd als ze van buitenaf worden gewijzigd? Een veelvoorkomende fout is de incorrecte vastlegging van een variabele in een lus.
Anonieme functies worden gedefinieerd als literals en kunnen aan variabelen worden toegewezen of direct worden gebruikt. Als een anonieme functie toegang heeft tot variabelen uit een externe scope, worden ze "gevangen" en opgeslagen voor de levensduur van de closure. Als parameter van een functie wordt een anonieme functie meestal doorgegeven met het type func dat compatibel is met de handtekening. De meeste problemen doen zich voor bij het vastleggen van lusvariabelen — in dat geval, als je de logica in een closure wilt wikkelen, maak dan altijd een nieuwe variabele binnen de lus aan.
Voorbeeld code:
func operate(nums []int, op func(int) int) []int { res := make([]int, len(nums)) for i, n := range nums { res[i] = op(n) } return res } func main() { arr := []int{1, 2, 3} out := operate(arr, func(x int) int {return x * x}) fmt.Println(out) // [1 4 9] }
Belangrijkste kenmerken:
Wat gebeurt er als je een variabele met een wijzigbaar waarde in een lus vastlegt via een anonieme functie?
Alle closures vangen dezelfde variabele, en bij het aanroepen van de functie na het verlaten van de lus krijg je dezelfde waarde.
Voorbeeld code:
func main() { a := []func(){} for i := 0; i < 3; i++ { a = append(a, func() { fmt.Println(i) }) } for _, f := range a { f() } // 3 3 3 }
Om dit te voorkomen, maak een nieuwe variabele in het lichaam van de lus:
for i := 0; i < 3; i++ { j := i a = append(a, func() { fmt.Println(j) }) // 0 1 2 }
Kunnen anonieme functies worden gebruikt als waarden van het type interface{}?
Functies zijn alleen compatibel met interface{}, niet met andere interfaces, ze kunnen niet met elkaar worden vergeleken (behalve nil). Als je een closure doorgeeft als interface{}, kun je deze alleen aanroepen door de type-conversie naar de func-handtekening.
Kunnen anonieme functies recursief zijn?
Ja, maar alleen als je eerst een naamvariabele voor de closure declareert, en vervolgens de functie eraan toewijst.
Voorbeeld code:
var fib func(n int) int fib = func(n int) int { if n < 2 { return n } return fib(n-1) + fib(n-2) } fmt.Println(fib(10)) // 55
In een lus door de lijst van callbacks verbindt de ontwikkelaar de handler als closure met de vastlegging van de iterator-variabele. Alle callbacks werken met een onjuist waarde, leidend tot bugs.
Voordelen:
Nadelen:
Binnen de lus wordt een nieuwe variabele voor iedere closure gemaakt, wat een correcte vastlegging van de waarde en verwacht gedrag garandeert.
Voordelen:
Nadelen: