Řízení pomocí odložit, panice a obnovit funkce
Teď se podíváme na některé řídicí toky, které jsou jedinečné pro Go: defer
, panic
a recover
. Každá z těchto funkcí má několik případů použití. Tady prozkoumáme nejdůležitější případy použití.
Odložit funkci
Příkaz v jazyce Go defer
odloží spuštění funkce (včetně parametrů), dokud funkce obsahující defer
příkaz nedokončí. Obecně platí, že funkci odložíte, když se chcete vyhnout zapomenutí úloh, jako je zavření souboru nebo spuštění procesu čištění.
Můžete odložit tolik funkcí, kolik chcete. Příkazy defer
se spouštějí v obráceném pořadí od posledního po první.
Podívejte se, jak tento model funguje, spuštěním následujícího ukázkového kódu:
package main
import "fmt"
func main() {
for i := 1; i <= 4; i++ {
defer fmt.Println("deferred", -i)
fmt.Println("regular", i)
}
}
Tady je výstup kódu:
regular 1
regular 2
regular 3
regular 4
deferred -4
deferred -3
deferred -2
deferred -1
V tomto příkladu si všimněte, že se při fmt.Println("deferred", -i)
každém odložení uložila hodnota i
a volání funkce se přidalo do fronty. Jakmile main()
funkce dokončí tisk regular
hodnot, spustila se všechna odložená volání. Všimněte si, že výstup z odložených volání je v obráceném pořadí (poslední, první ven), protože jsou přepnuty z fronty.
Typickým případem defer
použití funkce je zavření souboru po jeho použití. Tady je příklad:
package main
import (
"io"
"os"
"fmt"
)
func main() {
newfile, error := os.Create("learnGo.txt")
if error != nil {
fmt.Println("Error: Could not create file.")
return
}
defer newfile.Close()
if _, error = io.WriteString(newfile, "Learning Go!"); error != nil {
fmt.Println("Error: Could not write to file.")
return
}
newfile.Sync()
}
Po vytvoření nebo otevření souboru odložíte .Close()
funkci, aby se po dokončení nezapomněla soubor zavřít.
Funkce Panic
Chyby za běhu zpanikaří v programu Go, jako je například pokus o přístup k poli pomocí indexu mimo hranice nebo zrušení odvozování ukazatele na nil. Můžete také vynutit, aby program zpanikařil.
Integrovaná panic()
funkce zastaví normální tok řízení v programu Go. Když používáte panic
volání, všechna odložená volání funkcí běží normálně. Proces pokračuje v zásobníku, dokud se nevrátí všechny funkce. Program se pak chybově ukončí se zprávou protokolu. Tato zpráva obsahuje všechny informace o chybě a trasování zásobníku, které vám pomůžou diagnostikovat původní příčinu problému.
Při volání panic()
funkce můžete jako argument přidat libovolnou hodnotu. Obvykle odesíláte chybovou zprávu o tom, proč se zpanikaříte.
Následující kód například kombinuje funkce panic
a defer
funkce. Zkuste spustit tento kód a podívejte se, jak se tok řízení přeruší. Všimněte si, že procesy čištění stále běží.
package main
import "fmt"
func highlow(high int, low int) {
if high < low {
fmt.Println("Panic!")
panic("highlow() low greater than high")
}
defer fmt.Println("Deferred: highlow(", high, ",", low, ")")
fmt.Println("Call: highlow(", high, ",", low, ")")
highlow(high, low + 1)
}
func main() {
highlow(2, 0)
fmt.Println("Program finished successfully!")
}
Tady je výstup:
Call: highlow( 2 , 0 )
Call: highlow( 2 , 1 )
Call: highlow( 2 , 2 )
Panic!
Deferred: highlow( 2 , 2 )
Deferred: highlow( 2 , 1 )
Deferred: highlow( 2 , 0 )
panic: highlow() low greater than high
goroutine 1 [running]:
main.highlow(0x2, 0x3)
/tmp/sandbox/prog.go:13 +0x34c
main.highlow(0x2, 0x2)
/tmp/sandbox/prog.go:18 +0x298
main.highlow(0x2, 0x1)
/tmp/sandbox/prog.go:18 +0x298
main.highlow(0x2, 0x0)
/tmp/sandbox/prog.go:18 +0x298
main.main()
/tmp/sandbox/prog.go:6 +0x37
Program exited: status 2.
Co se stane, když se kód spustí:
Všechno běží normálně. Program vytiskne vysoké a nízké hodnoty předané do
highlow()
funkce.Pokud je hodnota větší než hodnota
low
high
, program zpanikaří. Zobrazí sePanic!
zpráva. V tomto okamžiku se tok řízení přeruší a všechny odložené funkce začnouDeferred...
zprávu tisknout.Program se chybově ukončí a zobrazí se úplné trasování zásobníku. Zprávu nevidíte
Program finished successfully!
.
Volání panic()
funkce se obvykle spustí, když se neočekává závažné chyby. Chcete-li zabránit chybovému ukončení programu, můžete použít jinou funkci s názvem recover()
.
Funkce Recover
Někdy se můžete chtít vyhnout chybovému ukončení programu a místo toho hlásit chybu interně. Nebo možná chcete před ukončením programu vyčistit nepořádek. Můžete například chtít zavřít jakékoli připojení k prostředku, abyste se vyhnuli dalším problémům.
Go poskytuje integrovanou recover()
funkci, která vám umožní znovu získat kontrolu po panice. Voláte recover
pouze funkci, ve které také voláte defer
. Pokud funkci zavoláte recover()
, vrátí nil
se a nemá žádný jiný účinek v normálním spuštění.
Zkuste upravit main
funkci v předchozím kódu, aby se přidalo volání recover()
funkce, například takto:
func main() {
defer func() {
handler := recover()
if handler != nil {
fmt.Println("main(): recover", handler)
}
}()
highlow(2, 0)
fmt.Println("Program finished successfully!")
}
Při spuštění programu by měl výstup vypadat takto:
Call: highlow( 2 , 0 )
Call: highlow( 2 , 1 )
Call: highlow( 2 , 2 )
Panic!
Deferred: highlow( 2 , 2 )
Deferred: highlow( 2 , 1 )
Deferred: highlow( 2 , 0 )
main(): recover from panic highlow() low greater than high
Program exited.
Vidíte rozdíl oproti předchozí verzi? Hlavním rozdílem je, že se už chyba trasování zásobníku nezobrazuje.
main()
Ve funkci odložíte anonymní funkci tam, kde funkci voláterecover()
. Volání, které recover()
se nepodaří vrátit nil
, když program zpanikaří. Tady můžete něco udělat, abyste vyčistili bordel, ale v tomto příkladu jednoduše něco vytisknete.
Kombinace panic
funkcí recover
je idiotitický způsob, jakým Go zpracovává výjimky. Blok používají try/catch
jiné programovací jazyky. Preferuje přístup, který jste zde prozkoumali.
Další informace najdete v návrhu pro přidání integrované try
funkce v Go.