赞
踩
fmt.Errorf函数以及后续对错误进行判断时使用到了 Unwrap 与 Is ,因此去了解一下 1.13 版本中 errors 包中引入的新特性。本文参考资料包括但不限于:
Go 1.13 errors 基本用法
golang关于errors.As两个参数的问题记录
在 1.13 引入了错误处理的新特性之后,我们可以对错误进行多次的封装,我们可以简单地把错误的封装理解为套娃,第一个错误套在第二个错误之中,第二个错误又可以套在第三个错误之中,如果你想的话可以将这个过程不断进行下去。
为了实现错误的"套娃",通常使用 fmt.Errorf 函数即可,除此之外也可以使用自定义结构体的方式实现(可参考 Go 1.13 errors 基本用法)
由于 fmt.Errorf 函数的使用比较方便,因此接下来都将使用该方法实现方法的封装。fmt.Errorf 方法使用 %w 参数返回一个被包装的 error。例如
err1 := errors.New("this is a error")
err2 := fmt.Errorf("this is a error including the first error: %w\n", err1)
通过该方法很容易就实现了对错误的封装。下面是另一个相似的例子
firstErr := errors.New("this is the first error")
secondErr := fmt.Errorf("this is the second error, it include the first error: %w", firstErr)
thirdErr := fmt.Errorf("this is the third error, it include the second error: %w", secondErr)
// 输出各个错误
fmt.Printf("%+v\n", firstErr) // this is the first error
fmt.Printf("%+v\n", secondErr) // this is the second error, it include the first error: this is the first error
fmt.Printf("%+v\n", thirdErr) // this is the third error, it include the second error: this is the second error, it include the first error: this is the first error
现在我们可以简单的实现错误的套娃了,此时自然也会有将套娃一层层打开的需求,此时就需要使用 errors.Unwrap 函数了。下面的例子可以很清楚的解释错误的封装与解封。
firstErr := errors.New("this is the first error") secondErr := fmt.Errorf("this is the second error, it include the first error: %w", firstErr) thirdErr := fmt.Errorf("this is the third error, it include the second error: %w", secondErr) // 输出各个错误 fmt.Printf("%+v\n", firstErr) // this is the first error fmt.Printf("%+v\n", secondErr) // this is the second error, it include the first error: this is the first error fmt.Printf("%+v\n", thirdErr) // this is the third error, it include the second error: this is the second error, it include the first error: this is the first error // Unwrap 函数测试 // 从 thirdErr 中解封各个错误 unwrapFirst := errors.Unwrap(thirdErr) unwrapSecond := errors.Unwrap(unwrapFirst) unwrapThird := errors.Unwrap(unwrapSecond) unwrapFourth := errors.Unwrap(unwrapThird) fmt.Println(unwrapFirst) // this is the second error, it include the first error: this is the first error fmt.Println(unwrapSecond) // this is the first error fmt.Println(unwrapThird) // <nil> fmt.Println(unwrapFourth) // <nil>
从例子中有两个发现:
errors.Is 方法判断被包装的 error 是否是包含指定错误,与直接使用 == 判断不同,Is 方法可以用于判断错误中是否包含指定的错误。Is 函数签名为:func Is(err, target error) bool,第一个参数为需要被判断的函数,第二个参数为目标错误,返回布尔类型变量表示是否包含指定错误。通过下面一个简单的例子去理解该函数
...(与前面代码一致)
// Is 函数测试
fmt.Println(errors.Is(firstErr, firstErr)) // true
fmt.Println(errors.Is(secondErr, firstErr)) // true
fmt.Println(errors.Is(thirdErr, firstErr)) // true
fmt.Println(errors.Is(firstErr, secondErr)) // false
fmt.Println(errors.Is(secondErr, secondErr)) // true
fmt.Println(errors.Is(thirdErr, secondErr)) // true
结果也很清晰
firstErr 作为目标错误,因此三个错误中都包含该错误,因此都返回 truesecondErr 作为目标错误,因此 firstErr 中不包含该错误,因此只有 firstErr 返回 false理解 As 方法首先需要理解 error 的定义,go 中 error 实际上就是一个包含 Error 方法的接口,只要实现了 Error 方法都可以作为 error。As 函数的作用就是从错误中获取第一个指定类型的错误。还是从实验中体验用法,该实验过程参考 golang关于errors.As两个参数的问题记录。需要注意一点:实际上As函数是在使用reflect包对两个参数的类型进行比较,其中第二个参数是第一个参数类型的指针类型
// 定义一个结构体用于实现 error 接口 type FuncErr struct { msg string code int } // 实现 error 接口 func (f FuncErr) Error() string { return f.msg } // 方便验证的调试方法 func (f FuncErr) PrintInfo() { fmt.Printf("msg: %s, code: %d\n", f.msg, f.code) } // 相当于我们自定义 error 的构造函数 func throw(s string, c int) error { return FuncErr{ msg: s, code: c, } } func WrappingError() { // 测试 errors.As 方法 // 1. 生成一个 FuncErr 类型的错误 funcErr := throw("this is a func error", 1) // 2. 将该错误包装两层 firstWrapped := fmt.Errorf("this is the first wrapped error that include %w", funcErr) secondWrapped := fmt.Errorf("this is the second wrapped error that include %w", firstWrapped) // 使用 errors.As 方法将错误解封到 FuncErr 类型 var targetError FuncErr // As 函数测试 fmt.Println(errors.As(funcErr, &targetError)) // true fmt.Println(targetError) // this is a func error targetError.PrintInfo() // msg: this is a func error, code: 1 fmt.Println(errors.As(firstWrapped, &targetError)) // true fmt.Println(targetError) // this is a func error targetError.PrintInfo() // msg: this is a func error, code: 1 fmt.Println(errors.As(secondWrapped, &targetError)) // true fmt.Println(targetError) // this is a func error targetError.PrintInfo() // msg: this is a func error, code: 1 }
这段程序中主要做了以下几件事:
FuncErr 并实现了 error 接口,即定义一个我们自己错误类型FuncErr 实例As 函数从中提取 FuncErr 类型的错误至此,基本上把 errors 中的新特性简单介绍完了。以下为本次实验的完整代码
package wrappingerror import ( "errors" "fmt" ) func WrappingError() { firstErr := errors.New("this is the first error") secondErr := fmt.Errorf("this is the second error, it include the first error: %w", firstErr) thirdErr := fmt.Errorf("this is the third error, it include the second error: %w", secondErr) // 输出各个错误 fmt.Printf("%+v\n", firstErr) // this is the first error fmt.Printf("%+v\n", secondErr) // this is the second error, it include the first error: this is the first error fmt.Printf("%+v\n", thirdErr) // this is the third error, it include the second error: this is the second error, it include the first error: this is the first error // Unwrap 函数测试 // 从 thirdErr 中解封各个错误 unwrapFirst := errors.Unwrap(thirdErr) unwrapSecond := errors.Unwrap(unwrapFirst) unwrapThird := errors.Unwrap(unwrapSecond) unwrapFourth := errors.Unwrap(unwrapThird) fmt.Println(unwrapFirst) // this is the second error, it include the first error: this is the first error fmt.Println(unwrapSecond) // this is the first error fmt.Println(unwrapThird) // <nil> fmt.Println(unwrapFourth) // <nil> // Is 函数测试 fmt.Println(errors.Is(firstErr, firstErr)) // true fmt.Println(errors.Is(secondErr, firstErr)) // true fmt.Println(errors.Is(thirdErr, firstErr)) // true fmt.Println(errors.Is(firstErr, secondErr)) // false fmt.Println(errors.Is(secondErr, secondErr)) // true fmt.Println(errors.Is(thirdErr, secondErr)) // true // 测试 errors.As 方法 // 1. 生成一个 FuncErr 类型的错误 funcErr := throw("this is a func error", 1) // 2. 将该错误包装两层 firstWrapped := fmt.Errorf("this is the first wrapped error that include %w", funcErr) secondWrapped := fmt.Errorf("this is the second wrapped error that include %w", firstWrapped) // 使用 errors.As 方法将错误解封到 FuncErr 类型 var targetError FuncErr // As 函数测试 fmt.Println(errors.As(funcErr, &targetError)) // true fmt.Println(targetError) // this is a func error targetError.PrintInfo() // msg: this is a func error, code: 1 fmt.Println(errors.As(firstWrapped, &targetError)) // true fmt.Println(targetError) // this is a func error targetError.PrintInfo() // msg: this is a func error, code: 1 fmt.Println(errors.As(secondWrapped, &targetError)) // true fmt.Println(targetError) // this is a func error targetError.PrintInfo() // msg: this is a func error, code: 1 } type FuncErr struct { msg string code int } func (f FuncErr) Error() string { return f.msg } func (f FuncErr) PrintInfo() { fmt.Printf("msg: %s, code: %d\n", f.msg, f.code) } func throw(s string, c int) error { return FuncErr{ msg: s, code: c, } }
在 main 中调用即可完成测试代码的执行
package main
import "godemo/wrappingerror"
func main() {
wrappingerror.WrappingError()
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。