GO 错误处理
概述
Go语言追求简洁优雅,所以,Go语言不支持传统的 try…catch…finally 这种异常,因为Go语言的设计者们认为,将异常与控制结构混在一起会很容易使得代码变得混乱。因为开发者很容易滥用异常,甚至一个小小的错误都抛出一个异常。在Go语言中,使用多值返回来返回错误。不要用异常代替错误,更不要用来控制流程。在极个别的情况下,也就是说,遇到真正的异常的情况下(比如除数为 0了)。才使用Go中引入的Exception处理:defer
, panic
, recover
。
这几个异常的使用场景可以这么简单描述:Go中可以抛出一个panic
的异常,然后在defer
中通过recover
捕获这个异常,然后正常处理。
当然不可能说,只处理真正的异常,那正常的逻辑错误,用户输入不符合预期这种错误怎么办?所以go 中 还有一个error
接口,一般来说:
-
error:一般用于表达可以被处理的错误
-
panic:一般用于表达非常严重不可恢复的错误
error本质
error只是一个内置的接口
我们可以去看看类型定义,在你 安装路径\src\builtin\builtin.go
,中可以找到
// The error built-in interface type is the conventional interface for
// representing an error condition, with the nil value representing no error.
type error interface {
Error() string
}
error 本质上是个接口,既然是个接口,那我们就可以自己实现,也就是说我们可以自定义 错误处理
自定义error 处理
例:
package main // 声明 main 包,表明当前是一个可执行程序
type MyError struct{}
// 实现error接口
func (m *MyError) Error() string {
return "这是自定义的错误"
}
func main() { // main函数,是程序执行的入口
// 假设这是用户的输入
input := 0
if input == 0 {
err := &MyError{}
println(err.Error())
return
}
println("这里是正常流程")
}
errors包
go 内置了一个 errors
,来帮我们快速处理错误相关的东西
- New() 创建一个新的 error
- Is() 判断是不是特定的某个error
- As() 类型转换为特定的error
- Unwrap() 解除包装,返回被包装的 error
例:
package main
import (
"errors"
"fmt"
)
type MyError struct {
}
func (m *MyError) Error() string {
return "Hello, it's my error"
}
func ErrorsPkg() {
// 创建一个error
// er := errors.New("this is new error")
err := &MyError{}
// 使用 %w 占位符,返回的是一个新错误
// wrappedErr 是一个新类型,fmt.wrapError
wrappedErr := fmt.Errorf("this is an wrapped error %w", err)
// 再解出来
if err == errors.Unwrap(wrappedErr) {
fmt.Println("unwrapped")
}
if errors.Is(wrappedErr, err) {
// 虽然被包了一下,但是 Is 会逐层解除包装,判断是不是该错误
fmt.Println("wrapped is err")
}
copyErr := &MyError{}
// 这里尝试将 wrappedErr转换为 MyError
// 注意我们使用了两次的取地址符号
if errors.As(wrappedErr, ©Err) {
fmt.Println("convert error")
}
}
func main() {
var err error = &MyError{}
println(err.Error())
ErrorsPkg()
}
error 和 panic 选用哪个?
- 遇事不决选 error
- 当你怀疑可以用 error 的时候,就说明你
- 不需要 panic 一般情况下,只有快速失败的过程,才会考虑panic
从 panic 中恢复
某些时候,你可能需要从 panic 中恢复过来:
比如某个库,发生 panic 的场景是你不希 望发生的场景。 这时候,你需要我们的 recover
如果你自己panic了,然后又要恢复过来,那么应该考虑 不要用panic了
例:
package main
import "fmt"
func main() {
defer func() {
if data := recover(); data != nil {
fmt.Printf("hello, panic: %v\n", data)
}
fmt.Println("恢复之后从这里继续执行")
}()
panic("Boom")
fmt.Println("这里将不会执行下来")
}