1.error到底为啥一直被讨论非议
1.1 没有堆栈信息
1.2 if err!=nil的烦人 调用不优雅
2.error相关的所有方法
2.1 errors包和接口
package errors
type error interface {
Error() string
}
// New returns an error that formats as the given text.
// Each call to New returns a distinct error value even if the text is identical.
func New(text string) error {
return &errorString{text}
}
// errorString is a trivial implementation of error.
type errorString struct {
s string
}
func (e *errorString) Error() string {
return e.s
}
2.2 errro的包装和解包装
wrapError:将原有的error类型包一层,且实现error接口方法,另外还有一个Unwrap方法 为啥要包一层呢?因为数据表达可能不够,比如堆栈信息没有
package main
import (
"errors"
"fmt"
)
func f1() error {
err := f2()
return fmt.Errorf("f1 error: %w", err) // %s -> %w
}
func f2() error {
err := f3()
return fmt.Errorf("f2 error: %w", err) // %s -> %w
}
func f3() error {
return errors.New("initial error")
}
func main() {
err := f1()
fmt.Println(err)
fmt.Println(errors.Unwrap(err)) // 调用Unwrap
fmt.Println(errors.Unwrap(errors.Unwrap(err))) // 调用Unwrap
}
// 输出如下:
// f1 error: f2 error: initial error
// f2 error: initial error //拨开一层
// initial error //再拨开一层
如何创建wrapError==>fmt.Errorf w% 原理:
func Errorf(format string, a ...any) error {
p := newPrinter()
p.wrapErrs = true
p.doPrintf(format, a)
s := string(p.buf)
var err error
if p. == nil {
err = errors.New(s)
} else {
err = &wrapError{s, p.wrappedErr}
}
p.free()
return err
}
func newPrinter() *pp {
p := ppFree.Get().(*pp)
p.panicking = false
p.erroring = false
p.wrapErrs = false
p.fmt.init(&p.buf)
return p
}
//pp is used to store a printer's state and is reused with sync.Pool to avoid allocations.
2.3 errors.Is(AError,BError),以及errors.As(AError,&otherError)
Is的判断分为两种情况,第一种:AError自己实现了 Is(error) bool 这个接口,那么它调用这个方法即可. 第二种情况:就是A包装了B这样的情况,如何判断就是递归Unwrap
if errors.Is(err, fs.ErrExist) {
// todo
}
var perr *fs.PathError
if errors.As(err, &perr) {
fmt.Println(perr.Path)
}
func Is(err, target error) bool {
if target == nil {
return err == target
}
isComparable := reflectlite.TypeOf(target).Comparable()
for {
if isComparable && err == target {
return true
}
if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(target) {
return true
}
if err = Unwrap(err); err == nil {
return false
}
}
}
func Unwrap(err error) error {
u, ok := err.(interface {
Unwrap() error
})
if !ok {
return nil
}
return u.Unwrap()
}
func As(err error, target any) bool {
if target == nil {
panic("errors: target cannot be nil")
}
val := reflectlite.ValueOf(target)
typ := val.Type()
if typ.Kind() != reflectlite.Ptr || val.IsNil() {
panic("errors: target must be a non-nil pointer")
}
targetType := typ.Elem()
if targetType.Kind() != reflectlite.Interface && !targetType.Implements(errorType) {// targetType实现了errorType
panic("errors: *target must be interface or implement error")
}
for err != nil {
if reflectlite.TypeOf(err).AssignableTo(targetType) { //确定当前类型err是否可分配给指定 targetType 的变量
val.Elem().Set(reflectlite.ValueOf(err))
return true
}
if x, ok := err.(interface{ As(any) bool }); ok && x.As(target) {
return true
}
err = Unwrap(err)
}
return false
}
如何调用其他的库,写自己的方法
既然error属于golang的异常必选题,那么我们的每个方法是否应该有一个error返回值值呢?
func m1() (int, error) {
time.Sleep(1 * time.Microsecond)
return 1, errors.New("error1")
}
func m2() (int, error) {
time.Sleep(2 * time.Microsecond)
return 2, errors.New("error2")
}
func test() (string, error) {
v1, err1 := m1()
if err1 != nil {
return "", err1
}
v2, err2 := m2()
if err2 != nil {
return "", err2
}
return strconv.Itoa(v1 + v2), nil
}
func test3() (string, error) {
v1, err1 := m1()
if err1 == nil {
v2, err2 := m2()
return strconv.Itoa(v1 + v2), err2
}
return "v1", err1
}
func test2() (string, error) {
var g errgroup.Group
c := make(chan int, 2)
g.Go(func() error {
v1, err1 := m1()
c <- v1
return err1
})
g.Go(func() error {
v2, err2 := m2()
c <- v2
return err2
})
var err error
if err = g.Wait(); err == nil {
fmt.Println("Successfully fetched all URLs.")
return "", nil
}
result := strconv.Itoa(<-c + <-c)
return result, err
}