golang进阶:为你的自定义error类增加errors.As、errors.Is的支持(兼容github.com/pkg/errors)

本文介绍了在Go中如何利用github.com/pkg/errors库实现类似于Java的错误处理机制,特别是自定义错误类型与pkg/errors的集成。通过示例展示了在错误包裹和提取过程中遇到的问题及解决方案,强调了自定义错误类型实现Unwrap方法的重要性,以确保Is和As操作的正确性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

java的cause error机制是个非常好用的东西,但在原生go包中没有那么到位的支持,使用github.com/pkg/errors能够基本重现该功能,但涉及自定义错误时仍不完美,还需手动做些补充——

例子

请看如下例子,本质是利用cause err层层包裹一个错误,既有使用pkg/errors包裹的部分,也有使用自定义错误类(MyCustomError)包裹的部分。

package errors

import (
	"errors"
	"fmt"
	"testing"

	errors2 "github.com/pkg/errors"
)

type BaseError struct {
	error
	b1 string
}

type MyCustomError struct {
	Msg   string
	cause error
}

func (e *MyCustomError) Error() string {
	if e.cause == nil {
		return e.Msg
	} else {
		return e.Msg + ":--cause:" + e.cause.Error()
	}
}

func TestErrorsAs(t *testing.T) {

	//模拟一个层叠包装的场景
	err := &BaseError{errors.New("原生底层err"), "a BaseError instance"}

	err2 := errors2.WithMessage(err, "使用pkg/errors包装的信息1")
	err3 := &MyCustomError{cause: err2, Msg: "中层,自定义error,统一规范"}
	err4 := errors2.WithStack(err3)
	err5 := errors2.WithMessage(err4, "使用pkg/errors包装的信息2")

	fmt.Println("//AS测试")
	fmt.Println("//取中间层测试:")
	var me *MyCustomError
	if errors.As(err5, &me) {
		fmt.Println("成功取出目标对象--", me)
	} else {
		fmt.Println("failure")
	}

	fmt.Println("//取底层测试:")
	var be *BaseError
	if errors.As(err5, &be) {
		fmt.Println("成功取出目标对象--:", be)
	} else {
		fmt.Println("failure")
	}

	fmt.Println("")
	fmt.Println("//IS测试:")

	if errors2.Is(err5, err3) {
		fmt.Println("IS err3")
	} else {
		fmt.Println("IS not err3")
	}
	if errors2.Is(err5, err) {
		fmt.Println("IS err5")
	} else {
		fmt.Println("IS not err5")
	}

}


附注:"github.com/pkg/errors"中的Is、As本质就是调用原生errors中的同名函数,所以这俩哪个方便就用哪个,没有区别。

运行结果如下:

=== RUN   TestErrorsAs
//AS测试
//取中间层测试:
成功取出目标对象-- 中层,自定义error,统一规范:--cause:使用pkg/errors包装的信息1: 原生底层err
//取底层测试:
failure

//IS测试:
IS err3
IS not err5

可以看到,使用pkg/errors的部分能够顺利完成判断和提取,而MyCustomError那层阻碍了AS和IS的判断。

解决方法

解决方法也非常简单,令MyCustomError实现Unwrap方法。

func (e *MyCustomError) Unwrap() error { return e.cause }

运行结果如下:

=== RUN   TestErrorsAs
//AS测试
//取中间层测试:
成功取出目标对象-- 中层,自定义error,统一规范:--cause:使用pkg/errors包装的信息1: 原生底层err
//取底层测试:
成功取出目标对象--: 原生底层err

//IS测试:
IS err3
IS err5

Is和As都能顺利返回预期的结果

小结

虽然是个一句话就解决的问题但其原理涉及动态语言、鸭子类型的应用,和java中通过继承来解决类似问题的思路有根本性的不同。感兴趣的可以自行搜索相关理论知识。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值