use_defer_statement
use_defer_statement
我们看看 Go 特有的一些控制流:defer、panic 和 recover。 Go 不支持异常。 它通过使用这三个函数的组合来处理运行时错误。
1 defer 函数
在 Go 中,defer 语句会推迟函数(包括任何参数)的运行,直到包含 defer 语句的函数完成。 通常情况下,当你想要避免忘记任务(例如关闭文件或运行清理进程)时,可以推迟某个函数的运行。 可以根据需要推迟任意多个函数。 defer 语句按逆序运行,先运行最后一个,最后运行第一个。可以理解是一个栈的结构, defer就是压栈
package main
import "fmt"
func main() {
for i := 1; i <= 4; i++ {
defer fmt.Println("deferred", -i)
fmt.Println("regular", i)
}
}
defer 函数的一个典型用例是在使用完文件后将其关闭 创建或打开某个文件后,可以推迟 .Close() 函数的执行,以免在你完成后忘记关闭该文件。
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()
}
2 panic 函数
运行时错误会使 Go 程序崩溃,例如尝试通过使用超出范围的索引或取消引用 nil 指针来访问数组。 你也可以强制程序崩溃。
内置 panic() 函数可以停止 Go 程序中的正常控制流。 当你使用 panic 调用时,任何延迟的函数调用都将正常运行。 进程会在堆栈中继续,直到所有函数都返回。 然后,程序会崩溃并记录日志消息。 此消息包含错误信息和堆栈跟踪,有助于诊断问题的根本原因。
调用 panic() 函数时,可以添加任何值作为参数。 通常,你会发送一条错误消息,说明为什么会进入紧急状态。
例如,下面的代码将 panic 和 defer 函数组合在一起。 尝试运行此代码以了解控制流的中断。 请注意,清理过程仍会运行(比方说io的缓冲的关闭)。
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!")
}
在发生未预料到的严重错误时,系统通常会运行对 panic() 函数的调用。 若要避免程序崩溃,可以使用名为 recover() 的另一个函数。
3 recover
有时,你可能想要避免程序崩溃,改为在内部报告错误。 或者,你可能想要先清理混乱情况,然后再让程序崩溃。 例如,你可能想要关闭与某个资源的连接,以免出现更多问题。
Go 提供内置 recover() 函数,让你可以在程序崩溃之后重新获得控制权。 你只会在你同时调用 defer 的函数中调用 recover。 如果调用 recover() 函数,则在正常运行的情况下,它会返回 nil,没有任何其他作用。
尝试修改前面的代码中的 main 函数,以添加对 recover() 的调用,如下所示:
func main() {
defer func() {
handler := recover()
if handler != nil {
fmt.Println("main(): recover", handler)
}
}()
highlow(2, 0)
fmt.Println("Program finished successfully!")
}
主要差异在于,你不再看到堆栈跟踪错误。
在 main() 函数中,你会将一个可以调用 recover() 函数的匿名函数推迟。 当程序处于紧急状态时,对 recover() 的调用无法返回 nil。 你可以在此处执行一些操作来清理混乱,但在本例中,你只是简单地输出一些内容。
panic 和 recover 函数的组合是 Go 处理异常的惯用方式。 其他编程语言使用 try/catch 块。 Go 首选此处所述的方法。