ProgrammatieBackend ontwikkelaar

Leg de eigenschappen uit van uitgestelde functiekparameters in Go: wanneer en hoe worden de parameters voor defer berekend, en hoe verschilt dit van het aanroepen van de functie op het moment van defer activatie?

Slaag voor sollicitatiegesprekken met de Hintsage AI-assistent

Antwoord.

Defer is een unieke mechanismen in Go die het mogelijk maakt om een functie uit te voeren na de voltooiing van de huidige functie, zelfs bij panics. Historisch gezien is dit vergelijkbaar met on-exit constructies, maar in Go is het geïmplementeerd als een stack van uitgestelde aanroepen. Een belangrijk detail is dat de parameters van de uitgestelde functie onmiddellijk worden berekend op het moment van het declareren van defer, niet op het moment van de daadwerkelijke aanroep!

Probleem — onduidelijk gedrag: men kan verwachten dat de parameters worden doorgegeven bij het activeren van defer, maar dat is niet zo. Dit leidt vaak tot bugs bij het werken met veranderlijke of externe variabelen.

Oplossing — altijd in gedachten houden dat de parameters onmiddellijk worden berekend, terwijl het effect van de uitgestelde aanroep later optreedt.

Voorbeeld code:

func f() { x := 5 defer fmt.Println(x) x = 10 } // Zal afdrukken: 5, en niet 10

Belangrijke kenmerken:

  • De waarden van de parameters voor uitgestelde functies worden berekend op het moment van het declareren van defer.
  • De uitgestelde functie wordt altijd uitgevoerd, zelfs bij panic (tenzij de panic eerder wordt onderschept).
  • Als je een anonieme functie in defer declareert, kun je toegang krijgen tot de actuele waarden van de variabelen.

Vragen met een twist.

Wat zal de volgende code afdrukken? Waarom?

func main() { i := 0 defer fmt.Println(i) i = 1 }

Antwoord: zal 0 afdrukken. Het argument van de functie fmt.Println wordt onmiddellijk opgeslagen bij het declareren van defer.

Heeft het wijzigen van de variabele na de declaratie van defer invloed op de overdracht van de waarde naar de functie?

Nee, het heeft geen invloed — de berekening gebeurt bij de declaratie van defer:

defer fmt.Println(x) // Waarde van x wordt nu opgeslagen, niet later

Kun je een defer maken om de laatste toestand van de variabele te tonen?

Ja, met behulp van een anonieme functie (closure):

defer func() { fmt.Println(x) }() // zal de actuele waarde van x vastleggen op het moment van uitgestelde aanroep

Typische fouten en anti-patronen

  • Verwachten dat de parameter actueel zal zijn op het moment van uitgestelde aanroep.
  • Gebruik van defer met parameters naast veranderlijke variabelen.
  • Verwarde stapels van defer zonder duidelijke beschrijving van de afhankelijkheden.

Voorbeeld uit het leven

Negatief geval

De code wordt op de volgende manier aangeroepen:

var f *os.File // ... defer f.Close()

Maar f wordt later toegewezen, dus panic door aanroep van nil pointer!

Voordelen:

  • Korte syntaxis als de variabele al is geïnitialiseerd.

Nadelen:

  • Als f == nil — panic.

Positief geval

Omhulling van een opruimactie via een anonieme uitgestelde functie met controle:

defer func() { if f != nil { f.Close() } }()

Voordelen:

  • Veiligheid en geen panics.

Nadelen:

  • Meer uitgebreide en "ruizige" syntaxis.