说明

Golang中引入error接口类型作为错误处理的标准模式,如果函数要返回错误,则返回值类型列表中肯定包含error;Golang中引入两个内置函数panic和recover来触发和终止异常处理流程,同时引入关键字defer来延迟执行defer后面的函数。

错误/异常处理的一些原则

错误处理的正确姿势
- 失败的原因只有一个时,不使用error
- 没有失败时,不使用error
- error应放在返回值类型列表的最后
- 错误值统一定义,而不是跟着感觉走
- 错误逐层传递时,层层都加日志
- 错误处理使用defer
- 当尝试几次可以避免失败时,不要立即返回错误
- 当上层函数不关心错误时,建议不返回error
- 当发生错误时,不忽略有用的返回值

异常处理的正确姿势
- 在程序开发阶段,坚持速错
- 在程序部署后,应恢复异常避免程序终止
- 对于不应该出现的分支,使用异常处理
- 针对入参不应该有问题的函数,使用panic设计

详见https://studygolang.com/articles/11753

error类型

// 此处返回值是error接口
func xxx() error {}


// error是一个接口
type error interface {
    Error() string
}

通用error

// errors.New
func New(text string) error {
	return &errorString{text}
}


// 
type errorString struct {
	s string
}

// 实现了Error方法,因此是error接口
func (e *errorString) Error() string {
	return e.s
}

  

err := errors.New("this is error")

自定义error

type myError struct {
	msg   string
	other string
}

// 实现Error方法
func (self *myError) Error() string {
	return fmt.Sprintf("%v %v", self.msg, self.other)
}

例子

package main

import "fmt"

type myError struct {
	msg   string
	other string
}

func (self *myError) Error() string {
	return fmt.Sprintf("%v %v", self.msg, self.other)
}

var (
	MYERROR1 = &myError{"myError1", "other thing1"}
	MYERROR2 = &myError{"myError222222", "other thing222222"}
)

func test1() (err error) {
	return MYERROR1
}

func test2() {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println(err)
		}
	}()
	panic(MYERROR2)
}

func main() {
	fmt.Println("######### test1 start ##############")
	err := test1()
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println("######### test1 end ##############")
	// ######### test1 start ##############
	// myError1 other thing1
	// ######### test1 end ##############

	fmt.Println()
	fmt.Println("######### test2 start ##############")
	test2()
	fmt.Println("######### test2 end ##############")
	// ######### test2 start ##############
	// myError222222 other thing222222
	// ######### test2 end ##############

}

 一些自定义的error

var (
    ErrSuccess           = StandardError{0, "成功"}
    ErrUnrecognized      = StandardError{-1, "未知错误"}
    ErrAccessForbid      = StandardError{1000, "没有访问权限"}
    ErrNamePwdIncorrect  = StandardError{1001, "用户名或密码错误"}
    ErrAuthExpired       = StandardError{1002, "证书过期"}
    ErrAuthInvalid       = StandardError{1003, "无效签名"}
    ErrClientInnerError  = StandardError{4000, "客户端内部错误"}
    ErrParamError        = StandardError{4001, "参数错误"}
    ErrReqForbidden      = StandardError{4003, "请求被拒绝"}
    ErrPathNotFount      = StandardError{4004, "请求路径不存在"}
    ErrMethodIncorrect   = StandardError{4005, "请求方法错误"}
    ErrTimeout           = StandardError{4006, "服务超时"}
    ErrServerUnavailable = StandardError{5000, "服务不可用"}
    ErrDbQueryError      = StandardError{5001, "数据库查询错误"}
)

//StandardError 标准错误,包含错误码和错误信息
type StandardError struct {
    ErrorCode int    `json:"errorCode"`
    ErrorMsg  string `json:"errorMsg"`
}

// Error 实现了 Error接口
func (err StandardError) Error() string {
    return fmt.Sprintf("errorCode: %d, errorMsg %s", err.ErrorCode, err.ErrorMsg)
}

  

Panic和recover

Go语言追求简洁优雅,所以,Go语言不支持传统的 try…catch…finally 这种异常,因为Go语言的设计者们认为,将异常与控制结构混在一起会很容易使得代码变得混乱。因为开发者很容易滥用异常,甚至一个小小的错误都抛出一个异常。在Go语言中,使用多值返回来返回错误。不要用异常代替错误,更不要用来控制流程。在极个别的情况下,也就是说,遇到真正的异常的情况下(比如除数为 0了)。才使用Go中引入的Exception处理:defer, panic, recover。

这几个异常的使用场景可以这么简单描述:Go中可以抛出一个panic的异常,然后在defer中通过recover捕获这个异常,然后正常处理。

 

语法

panic
// 手动抛出异常
panic(v interface{})

// recover捕捉异常
if err:=recover();err!=nil{
            fmt.Println(err) // 这里的err其实就是panic传入的内容
        }

例子

package main

import "fmt"

func testPanic(){
	// 使用了recover,则本func会panic掉,后面的代码会继续执行
	// 如果不使用recover,则整个程序都会panic掉
	defer func(){
		if err := recover();err != nil{
			fmt.Println(err)
		}
	}()
	panic("haha")

	// 上面panic,这里就不会执行了
	fmt.Println("end")
}

func main() {
	// 由于使用了recover捕捉panic,所以该func执行后还会执行后面代码
	testPanic()
	fmt.Println("main end")
}