GoSuda

Hvorfor har ikke Go Try-Catch?

By Rabbit Princess
views ...

Go støtter ikke try-catch med hensikt, men kun panic-recover-syntaksen. Dette har vakt harme hos mange utviklere som er vant til feilhåndtering ved hjelp av try-catch. Hva er så grunnen til at try-catch ikke inkluderes? Det skyldes at try-catch har flere problematiske aspekter.

try-catch-finally

Try-catch-konstruksjonen er en syntaks for å håndtere feilsituasjoner og unntak som kan oppstå under kjøring (runtime) av et program. Finally-konstruksjonen brukes til å skrive kode som må utføres ubetinget, uavhengig av om det oppstår et unntak eller ikke.

Feilhåndtering og ansvar

På 80- og 90-tallet var feilhåndtering svært enkel. Feilmeldinger var begrenset til 'Floppydisken er full', 'Ingen floppydisk i stasjonen', 'Ingen skrivetillatelse for floppydisken', og utviklere på den tiden kastet (throw) feilen til feilhåndteringspunktet for å behandle den på en felles måte. I slike situasjoner fungerte try-catch-konstruksjonen effektivt.

Men etter hvert som tiden gikk, endret situasjonen seg. Forretningslogikken ble mer kompleks, databaser og transaksjoner oppsto, utallige API-er ble kalt over nettverket, og et stort antall forespørselsmeldinger måtte tolkes. Til og med fremveksten av samtidig programmering krevde at feil ble håndtert i andre tråder enn hovedtråden.

Feil ble såpass komplekse at de ikke lenger kunne behandles på ett sted, og ingen enkelt instans kunne ta ansvar for alle feil. Her oppstår et alvorlig problem med try-catch.

Ansvarsfraskrivelse med try-catch

Try-catch er, enkelt sagt, en metode der den som utløste feilen, skyver ansvaret (oppryddingen) for feilen over på noen andre. Målet for denne ansvarsfraskrivelsen kan være catch-konstruksjonen, dens overordnede metode, eller forelderen til forelderen til forelderen til forelderen... med andre ord, noen. I en verden der feilhåndtering blir mer utbredt og kompleks, er try-catch sin tilnærming rett og slett 'Noen vil nok ta seg av det'. Se på koden nedenfor.

1try {
2    data = readFile("hello.txt"); //leser filen hello.txt
3    structuredData = parseData(data); //parser data
4    insertDBStatus(structuredData[1]); //setter databasestatus
5    startHTTPServer(structuredData[2]); //starter en tjener
6} catch (Exception e) {
7    e.printStackTrace();
8}

Problemet med koden ovenfor er at det er uklart hvem som er ansvarlig for å håndtere printStackTrace, og det er umulig å vite hvilken kode som forårsaket feilen. Problemet blir enda verre jo mer logikk som legges til try-instruksjonen. Men paradoksalt nok, jo mer kompleks utviklingen blir, desto mer avhengige blir utviklere av try-catch-konstruksjonen som fraskriver seg ansvar, de unnlater å tenke over feilhåndtering, ansvarsfølelsen svekkes, og til slutt glemmer de essensen av feil- og unntaksbehandling. Så hvordan løste golang dette problemet?

panic, recover

En av fordelene med Go er at det har flere systemer som er designet for å hindre utviklere i å ta dårlige valg og for å gjøre dem til gode utviklere. Panic - recover er et eksempel på dette.

Panic og recover ser ved første øyekast ikke ut til å være forskjellige fra try-catch, men forskjellen ligger i at de ikke skyver ansvaret utover når et problem oppstår. Når en panic oppstår i Go, bør den løses der den oppsto, i stedet for å returnere verdien eksternt. Dette gir utvikleren ansvar for feilhåndtering og oppfordrer dem til å tenke grundigere over hvor, hvem og hvordan feilen skal håndteres. Det gir brukeren autonomi samtidig som det gir rom for refleksjon.

Valget av ordet panic er også svært treffende. I motsetning til try-recover gir panic-recover utvikleren et press bare ved hjelp av ordene til å bruke det kun i klare feilsituasjoner, og ikke i unntakssituasjoner. Naturlig nok vil utvikleren ikke misbruke recover og kun bruke det der det er nødvendig. Dette er til stor hjelp for å skrive konsis kode i Go.