Go语言中,"异常"的概念与其他语言如Java或Python中的不太相同。Go没有提供传统意义上的try-catch
机制来捕获异常。相反,Go使用错误作为其错误处理的主要方式
感觉很麻烦,到处都要处理…,没有传统的try-catch
好用
func SomeFunction() (int, error) {
// ... function logic ...
// When an error occurs
if somethingWentWrong {
return 0, errors.New("something went wrong")
}
// No error occurred
return result, nil
}
func main() {
result, err := SomeFunction()
if err != nil {
// handle the error
fmt.Println("Error occurred:", err)
} else {
// no error, use the result
fmt.Println("Success:", result)
}
}
package main
import (
"fmt"
"os"
)
func init() {
// 读取不到配置文件
_, err := os.ReadFile("config.yaml")
if err != nil {
panic(err.Error()) // panic 函数会立即停止当前函数的执行
// 或者使用 log.Fatalln("配置文件读取失败")
}
}
func main() {
fmt.Println("Hello, World!")
}
这个例子中,panic函数会立即停止当前函数的执行,然后逐层向上返回,直到被程序的顶层调用者捕获,这时程序通常会崩溃,抛出panic:panic: open config.yaml: The system cannot find the file specified.
或者使用 log.Fatalln()
,它里面会执行os.Exit(1)
,程序也会立马终止,但可以自定义提示信息
defer
语句配合recover
函数来捕获和处理这种类型的错误recover
是一个内置函数,用于"捕获"或"恢复"一个panic
。recover
只有在defer
语句中调用时才有效。如果在没有发生panic
的情况下调用recover
,或者不在defer
语句中调用recover
,它将不会有任何效果,并返回nil
当代码执行到panic
时,正常的函数执行流程会被中断,然后Go运行时会开始逐层向上回退(unwind)调用栈,运行每一层的defer
语句。如果在这个过程中某个defer
语句中调用了recover
,recover
会捕获到panic
的值,并且阻止panic
继续向上传播。这样,程序就有机会从错误中恢复,继续执行
看个例子:
package main
import "fmt"
func mayPanic() {
panic("a problem")
}
func main() {
defer func() {
if r := recover(); r != nil {
// 处理错误
fmt.Println("Recovered. Error:\n", r)
// 处理完错误,然后下面执行正常逻辑代码
// 正常代码
}
}()
mayPanic()
// 这行代码只有在recover成功捕获panic后才会运行
fmt.Println("After mayPanic()")
}
mayPanic函数调用panic,此时main函数中的defer语句会被执行。在这个defer语句中,recover函数被调用并捕获了panic的值。因此,程序从panic中恢复过来,而不是终止执行。然后程序会继续执行main函数中defer之后的代码。如果没有recover捕获panic,程序将会终止,并打印出panic信息和堆栈跟踪
处理错误时,可能希望查看具体是哪里执行出错,可以使用
fmt.Println(string(debug.Stack()))
它会打印详细的调用栈信息,可以具体到出错的行
进一步,我们单独弄个函数来处理
package main
import "fmt"
func mayPanic() {
panic("a problem")
}
func dealWithPanic() {
defer func() {
if r := recover(); r != nil {
// 处理错误
fmt.Println("Recovered. Error:\n", r)
}
}()
mayPanic()
}
func main() {
dealWithPanic()
// 正常要处理的代码
fmt.Println("After mayPanic()")
}