先记录初步的想法,后续完善。 对于真正意外的情况,表示不可恢复的程序错误,例如索引越界、不可恢复的环境问题,栈溢出,我们才使用panic。 对于其他的错误情况,我们应该期望使用error来进行判定。
1. panic
panic的时机:
- 在程序启动时,如果强依赖服务出现故障,应该
panic退出。 - 在程序启动时,如果发现加载的配置明显不符合要求,应该
panic退出(防御编程)。 - 其他时候,如果不是不可恢复的错误,不应该使用
panic,应该返回error。 - 在程序入口处应该对
panic进行recover,防止系统退出;如gin的recovery中间件。 - 在程序中我们应该避免使用野生
goroutine,应该使用同一的 Go 函数进行创建,这个函数中会进行 recover ,避免因为野生 goroutine panic 导致主进程退出。 ```go
// 仅限于无参的函数,如果是有参的,做不到同一公用 func NewGo(f func()) { go func(){ defer func() { if err := recover(); err != nil { log.Printf(“panic: %+v”, err) } } f() }() }
<a name="a0Wip"></a>## 2. error Type`sentinel error`<br />哨兵错误,简单说就是预定义的错误,比如标准库的`io.EOF`、`sql.ErrNoRows``error Type`<br />自定义错误类型,实现`error interface``opaque error`<br />不透明错误,内部定义一个私有接口,公开一个判断错误的行为函数,让外部断言行为,避免引入`error`包```gotype temporary interface {Temporaray() bool}func IsTemporary(err error) bool {te, ok := err.(temporary)return ok && te.Temporary()}
3. handling error
error的处理时机:
- 我们应该在应用程序中使用
github.com/pkg/errors,避免在公共库中使用。 error是函数的最后一个返回值,当err != nil,函数的其他返回值是不可用的,不应该对其他返回值抱有期待。- 函数出现
error应该只做一次处理(直接返回或降级处理),如果需要携带信息,使用pkg/errors.WithMessage 如果是应用程序外的其他库(标准库,第三方库)出现
error,应该使用pkg/errors.Wrap添加堆栈信息,而且只在该错误第一次出现的地方使用Wrapfunc (u *User)Get(db gorm*DB) (*User, error) {var user Usererr := db.First(&user).Errorif err != nil {return nil, errors.Wrap(err, "get user failed")}return &user, nil}
使用标准库的
errors.Is判断错误func f() error {err := A()if errors.Is(err, io.EOF) {// ...}return nil}
使用标准库的
errors.As给错误类型赋值func f() error {err := A()var errA errorAif errors.As(err, &errA) {// ...}return nil}
