NOTE

1.3 error

1. exception和error Golang把错误分成exception和error - exception是严程序内部的BUG或者不可预料的情况 - error则是可以预期的,通过一个返回值来判断 2. 使用 2.1. exception - 内部BUG通过recovery捕获转换成erro

Go创建于 更新于 historical

这是历史学习笔记,可能存在过时或不完整的理解。

1. exception和error

Golang把错误分成exception和error

  • exception是严程序内部的BUG或者不可预料的情况
  • error则是可以预期的,通过一个返回值来判断

2. 使用

2.1. exception

  • 内部BUG通过recovery捕获转换成error处理
  • 不可预料的就通过panic终止程序了

2.2. error

  • 通过fmt.ErrorOf或者errs.New构造err
func TestError1(t *testing.T) {
	//要求传入的字符串首字母小写,结尾不带标点符号
	fmt.Println(errors.New("network cannot reachable"))
	fmt.Println(fmt.Errorf("value %s too long", "aaaaaaaaaaa"))
}
//输出
network cannot reachable
value aaaaaaaaaaa too long
  • 如何处理error
    • 使用github.com/pkg/errors
    • 原则
      • 不要重复处理。比如下层捕获error后打印了,又往上层抛出
      • 不要仅检查错误。比如下层往上抛出error,上层捕获到之后不要原封不动的往外抛,而是应该加上信息
      • 不要根据error string确定是否某个error。虽然可以通过转换类型来判断是否具体的类型,但更推荐定义错误码

2.2.1. 例子

  • main.go
package main

import (
	"database/sql"
	"fmt"
	"github.com/pkg/errors"
	"log"
	"test/errors/service"
)



func main() {
	err := service.FindUser(1)
	if errors.Cause(err) == sql.ErrNoRows {
		fmt.Printf("data not found, %v\n", err)
		return
	}
	if err != nil {
		log.Fatalf("%+v\n", err)
		return
	}

	log.Println("---------------------------")
}

//输出
//如果是0
data not found, service finduser failed: dao find user faile
d: sql: no rows in result set

//如果是1
val cannot be 1
test/errors/dao.SelectOne
        C:/Users/zsk/code/go/test/errors/dao/dao.go:12
test/errors/service.FindUser
        C:/Users/zsk/code/go/test/errors/service/service.go:9
main.main
        C:/Users/zsk/code/go/test/errors/errors.go:32
runtime.main
        C:/software/Go/src/runtime/proc.go:203
runtime.goexit
        C:/software/Go/src/runtime/asm_amd64.s:1357
service finduser failed


//如果是2
---------------------------
  • service.go
package service

import (
	"github.com/pkg/errors"
	"test/errors/dao"
)

func FindUser(val int) error {
	return errors.WithMessage(dao.SelectOne(val), "service finduser failed")
}
    
  • dao.go
package dao

import (
	"database/sql"
	"github.com/pkg/errors"
)

func SelectOne(val int) error {
	if val == 0 {//封装底层error
		return errors.Wrap(sql.ErrNoRows, "dao find user failed")
	} else if val == 1 {//自定义error
		return errors.Errorf("val cannot be %d", val)
	}
	return nil
}

3. 原理

3.1. 数据结构

type error interface {
    Error() string
}

3.2. 方法

// src/errors/errors.go

func New(text string) error {
    //创建了errorString对象
	return &errorString{text}
}

//只包含了错误信息
type errorString struct {
	s string
}

func (e *errorString) Error() string {
	return e.s
}


func Errorf(format string, a ...interface{}) error {
    //先格式化字符串
	p := newPrinter()
	p.wrapErrs = true
	p.doPrintf(format, a)
	s := string(p.buf)
	var err error
	if p.wrappedErr == nil {
	    //再通过errors.New创建
		err = errors.New(s)
	} else {
		err = &wrapError{s, p.wrappedErr}
	}
	p.free()
	return err
}

4. 最佳实践

go get -u golang.org/x/xerrors
  • main
func main() {
	err := service.FindUser(1)
	/* 
	2022/10/30 16:08:16 预定义错误: query user failed:
		test/errors/service.FindUser
			C:/Users/zsk/code/go/test/errors/service/service.go:11
	- check val [0] failed:
		test/errors/dao.checkVal
			C:/Users/zsk/code/go/test/errors/dao/dao.go:23
	- invalid val
	exit status 1
	*/
	if errors.Is(err, dao.ErrorInvalidVal) {
		log.Fatalf("预定义错误: %+v\n", err)
	}
	/* 
	2022/10/30 16:09:15 非预定义错误: query user failed:
		test/errors/service.FindUser
			C:/Users/zsk/code/go/test/errors/service/service.go:11
	- record not found for val [1]:
		test/errors/dao.SelectOne
			C:/Users/zsk/code/go/test/errors/dao/dao.go:16
	exit status 1
	*/
	if err != nil {
		log.Fatalf("非预定义错误: %+v\n", err)
	}
	log.Println("-------------success--------------")
}
  • service
func FindUser(val int) error {
	if err := dao.SelectOne(val); err != nil {
		return xerrors.Errorf("query user failed: %w", err)
	}
	return nil
}
  • dao
var ErrorInvalidVal = errors.New("invalid val")

func SelectOne(val int) error {
	if err := checkVal(val); err != nil {
		return err
	}
	if val == 1 {
		return xerrors.Errorf("record not found for val [%v]", val)
	}
	return nil
}

func checkVal(val int) error {
	if val == 0 {
		return xerrors.Errorf("check val [%d] failed: %w", val, ErrorInvalidVal)
	}
	return nil
}

5. 参考