Error Wrap 高级使用分析

467 阅读2分钟

如何判断是否相等

go相同类型的结构体可以进行之比较,定义相同的结构体可以强转后比较

指针比较是值比较,也就是是否为相同地址值

errors.Is()方法

package main

import (
	"errors"
	"fmt"
	"reflect"
)

func main() {
	TestIs()
}

type wrapped struct {
	msg string
	err error
}

func (e wrapped) Error() string { return e.msg }

func (e wrapped) Unwrap() error { return e.err }
func TestIs() {
	err1 := errors.New("1")
	fmt.Printf("%p %p\n", err1, &err1)
	erra := wrapped{"wrap 2", err1}
	fmt.Printf("%p %p\n", erra, &erra)
	errb := wrapped{"wrap 3", erra}
	fmt.Printf("%p %p\n", errb, &errb)
	unwrap := errors.Unwrap(errb)
	fmt.Printf("%p %p\n", unwrap, &unwrap)
	fmt.Println(reflect.TypeOf(unwrap))
	fmt.Println(reflect.TypeOf(erra))
	fmt.Println(reflect.TypeOf(errb.err))

	testCases := []struct {
		err    error
		target error
		match  bool
	}{
		//{err1, err1, true},
		//{erra, err1, true},
		//{errb, err1, true},
		{errb, erra, true},
	}
	for _, tc := range testCases {
		got := errors.Is(tc.err, tc.target)
		fmt.Println(got)
	}
}
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
		}
		// TODO: consider supporting target.Is(err). This would allow
		// user-definable predicates, but also may allow for coping with sloppy
		// APIs, thereby making it easier to get away with them.
		if err = Unwrap(err); err == nil {
			return false
		}
	}
}

结果

0xc000010210 0xc000010200
%!p(main.wrapped={wrap 2 0xc000010210}) 0xc00000c060
%!p(main.wrapped={wrap 3 {wrap 2 0xc000010210}}) 0xc00000c0a0
%!p(main.wrapped={wrap 2 0xc000010210}) 0xc000010220
main.wrapped
main.wrapped
main.wrapped
true

可以看出wrapa 和 unwrap 的地址不同,但是值相同,所以判断为相等.如果是指针类型则做指针是否相等判断

errors.As()方法

package main

import (
	"os"
	"errors"
	"fmt"
)

func main() {
	if _, err := os.Open("non-existing"); err != nil {
		var pathError *os.PathError
		errNew := fmt.Errorf("wrapper~~ %w", err)
		if errors.As(errNew, &pathError) {
			fmt.Println("Failed at path:", pathError.Path)
		} else {
			fmt.Println(err)
		}
	}
}
Failed at path: non-existing

遍历errNew unwrap到target类型的值后赋值到target指向地址

参考

代码改造自官方文档,写作来源来自下面地址:

[Go语言(golang)新发布的1.13中的Error Wrapping深度分析](www.cnblogs.com/sunsky303/p…)