go error接口与errors包详解
文章目录
- 错误包需要具有哪些功能?
- 1. 应该能支持错误堆栈
- 2. 能够支持不同的打印格式
- 3. 能支持 Wrap/Unwrap 功能,也就是在已有的错误上,追加一些新的信息
- 4. 错误包应该有Is方法
- 5. 错误包应该支持 As 函数
- 1.1 常见调用方式
- 1.2 自定义error方法
- 1.2.1 函数调用error
- 1.2.2 自定义Error模板1
- 1.2 自定义Error模板2
- 2 errors包
- 2.1 获取error包
- 2.2 errors.New()
- 3 自定义error与errors.New()使用比较
错误包需要具有哪些功能?
1. 应该能支持错误堆栈
我们来看下面一段代码,假设保存在bad.go文件中:
package mainimport ("fmt""log"
)func main() {if err := funcA(); err != nil {log.Fatalf("call func got failed: %v", err)return}log.Println("call func success")
}func funcA() error {if err := funcB(); err != nil {return err}return fmt.Errorf("func called error")
}func funcB() error {return fmt.Errorf("func called error")
}
执行上面的代码:
$ go run bad.go
2021/07/02 08:06:55 call func got failed: func called error
exit status 1
这时我们想定位问题,但不知道具体是哪行代码报的错误,只能靠猜,还不一定能猜到。为了解决这个问题,我们可以加一些 Debug 信息,来协助我们定位问题。这样做在测试环境是没问题的,但是在线上环境,一方面修改、发布都比较麻烦,另一方面问题可能比较难重现。这时候我们会想,要是能打印错误的堆栈就好了。例如:
2021/07/02 14:17:03 call func got failed: func called error
main.funcB/home/colin/workspace/golang/src/github.com/marmotedu/gopractise-demo/errors/good.go:27
main.funcA/home/colin/workspace/golang/src/github.com/marmotedu/gopractise-demo/errors/good.go:19
main.main/home/colin/workspace/golang/src/github.com/marmotedu/gopractise-demo/errors/good.go:10
runtime.main/home/colin/go/go1.16.2/src/runtime/proc.go:225
runtime.goexit/home/colin/go/go1.16.2/src/runtime/asm_amd64.s:1371
exit status 1
通过上面的错误输出,我们可以很容易地知道是哪行代码报的错,从而极大提高问题定位的效率,降低定位的难度。所以,在我看来,一个优秀的 errors 包,首先需要支持错误堆栈。
2. 能够支持不同的打印格式
例如%+v、%v、%s等格式,可以根据需要打印不同丰富度的错误信息。
3. 能支持 Wrap/Unwrap 功能,也就是在已有的错误上,追加一些新的信息
例如errors.Wrap(err, "open file failed")
。Wrap 通常用在调用函数中,调用函数可以基于被调函数报错时的错误 Wrap 一些自己的信息,丰富报错信息,方便后期的错误定位,例如:
func funcA() error {if err := funcB(); err != nil {return errors.Wrap(err, "call funcB failed")}return errors.New("func called error")
}func funcB() error {return errors.New("func called error")
}
这里要注意,不同的错误类型,Wrap 函数的逻辑也可以不同。另外,在调用 Wrap 时,也会生成一个错误堆栈节点。我们既然能够嵌套 error,那有时候还可能需要获取被嵌套的 error,这时就需要错误包提供Unwrap
函数。
4. 错误包应该有Is方法
在实际开发中,我们经常需要判断某个 error 是否是指定的 error。在 Go 1.13 之前,也就是没有 wrapping error
的时候,我们要判断 error 是不是同一个,可以使用如下方法:
if err == os.ErrNotExist {// normal code
}
5. 错误包应该支持 As 函数
在 Go 1.13
之前,没有 wrapping error
的时候,我们要把 error
转为另外一个 error
,一般都是使用 type assertion
或者 type switch
,也就是类型断言。例如:
if perr, ok := err.(*os.PathError); ok {fmt.Println(perr.Path)
}
但是现在,返回的 err 可能是嵌套的 error,甚至好几层嵌套,这种方式就不能用了。所以,我们可以通过实现 As 函数来完成这种功能。现在我们把上面的例子,用 As 函数实现一下:
var perr *os.PathError
if errors.As(err, &perr) {fmt.Println(perr.Path)
}
这样就可以完全实现类型断言的功能,而且还更强大,因为它可以处理 wrapping error
。
最后,能够支持两种错误创建方式:非格式化创建和格式化创建。例如:
errors.New("file not found")
errors.Errorf("file %s not found", "iam-apiserver")
上面,我们介绍了一个优秀的错误包应该具备的功能。一个好消息是,Github 上有不少实现了这些功能的错误包,其中github.com/pkg/errors
包最受欢迎。所以,我基于github.com/pkg/errors
包进行了二次封装。
1.1 常见调用方式
模板
n, err := Foo(0) if err != nil { // 错误处理
} else { // 使用返回值 n
}
练习1
package mainimport ( "fmt""os"
)func main() { f, err := os.Open("/test.txt")if err != nil {fmt.Println(err)return}fmt.Println(f.Name(), "opened successfully")
}
[root@localhost error]# go run err3.go
open /test.txt: no such file or directory
1.2 自定义error方法
1.2.1 函数调用error
func Foo(param int)(n int, err error) { // ...
}
1.2.2 自定义Error模板1
type fileError struct {}func (fe *fileError) Error() string {return "文件错误"
}
练习1
模拟一个错误
package mainimport "fmt"type fileError struct {}func (fe *fileError) Error() string { //自定义会覆盖原来的Error接口return "文件错误"
}//只是模拟一个错误
func openFile() ([]byte, error) {return nil, &fileError{}
}func main() {conent, err := openFile()if err != nil {fmt.Println(err)} else {fmt.Println(string(conent))}
}
[root@localhost error]# go run err1.go
文件错误
1.2 自定义Error模板2
自定义中添加一个字符串
type fileError struct {s string
}func (fe *fileError) Error() string {return fe.s
}
练习2
声明fileError的时候,设置好要提示的错误文字
package mainimport "fmt"type fileError struct {s string
}func (fe *fileError) Error() string {return fe.s
}//只是模拟一个错误
func openFile() ([]byte, error) {return nil, &fileError{"文件错误,自定义"}
}func main() {conent, err := openFile()if err != nil {fmt.Println(err)} else {fmt.Println(string(conent))}
}
[root@localhost error]# go run err2.go
文件错误,自定义
练习3
添加一个时间刻度
package mainimport ("fmt""time"
)type MyError struct {When time.TimeWhat string
}func (e MyError) Error() string {return fmt.Sprintf("%v: %v", e.When, e.What)
}func oops() error {return MyError{time.Date(1989, 3, 15, 22, 30, 0, 0, time.UTC),"the file system has gone away",}
}func main() { if err := oops(); err != nil { fmt.Println(err) }
}
[root@localhost error]# go run err4.go
1989-03-15 22:30:00 +0000 UTC: the file system has gone away
练习三
没有打开文件报错
package mainimport ("fmt""os"
)type PathError struct {Op stringPath stringErr error
}func (e *PathError) Error() string { return e.Op + " " + e.Path + ": " + e.Err.Error()
} func main() {f, err := os.Open("/test.txt")if errObject, ok := err.(*os.PathError); ok {fmt.Println("错误输出:",err, "文件路径:", errObject.Path)return}fmt.Println(f.Name(), "opened successfully")
}
[root@localhost error]# go run err5.go
错误输出: open /test.txt: no such file or directory 文件路径: /test.txt
2 errors包
2.1 获取error包
go get github.com/pkg/errors
2.2 errors.New()
errors.New()接收合适的错误信息来创建
先声明再使用
l练习1
package mainimport ("errors""fmt"
)var errNotFound error = errors.New("Not found error")func main() {fmt.Printf("error: %v", errNotFound)
}
[root@localhost error]# go run errs1.go
error: Not found error
练习2
函数如何调用err
直接使用
package mainimport ("errors""fmt"
)func Sqrt(f float64) (float64, error) {if f < 0 {return 0, errors.New("math - square root of negative number")}else {return 1, errors.New("math - square root of 10")}
}func main() {if _, err := Sqrt(-1); err != nil {fmt.Printf("Error: %s\n", err)}
}
[root@localhost error]# go run errs2.go
Error: math - square root of negative number
3 自定义error与errors.New()使用比较
比较自定义error与errors.New()函数根据需求其实各有优点。
package mainimport ("errors""fmt"
)type MsgError struct {Code intMsg string
}
func (msg *MsgError) Error() string {return fmt.Sprintf("%s", msg.Msg)
}func f1(code int) (int, error) {if code == 1 {return -1, errors.New("msg test error")}return code, nil
}func f2(code int) (int, error) {if code == 1 {return -1, &MsgError{code, "struct msg test error"}}return code, nil
}func main() {for _, v := range []int{1, 2, 3, 4, 5, 6} {if code, err := f1(v); err != nil {fmt.Println(err)} else {fmt.Println("success:", code)}}for _, i := range []int{1, 2, 3} {if code, err := f2(i); err != nil {fmt.Println(err)} else {fmt.Println("success:", code)}}
}
[root@localhost error]# go run errs3.go
msg test error
success: 2
success: 3
success: 4
success: 5
success: 6
struct msg test error
success: 2
success: 3
go error接口与errors包详解相关推荐
- 【spring】jar包详解与模块依赖关系
以spring3.X为例 jar包详解 1. spring-core.jar:包含Spring框架基本的核心工具类,Spring其它组件要都要使用到这个包里的类,是其它组件的基本核心: 2. spri ...
- spring-jar包详解整理
Spring各jar包详解 spring.jar 是包含有完整发布模块的单个jar 包.但是不包括mock.jar,aspects.jar, spring-portlet.jar, and sprin ...
- springframework-jar包详解
springframework-jar包详解 org.springframework.aop --Spring的面向切面编程,提供AOP(面向切面编程)的实现 org.springframework. ...
- android jar 包 意见反馈功能,android重点jar包详解.docx
android重点jar包详解 深入理解View(一):从setContentView谈起 我们都知道?MVC,在Android中,这个?V?即指View,那我们今天就来探探View的究竟.在onCr ...
- java axis2 jar_Java axis2.jar包详解及缺少jar包错误分析
Java axis2.jar包详解及缺少jar包错误分析 一.最小开发jar集 axis2 开发最小jar包集: activation-1.1.jar axiom-api-1.2.13.jar ax ...
- STM32接口FSMC/FMC难点详解
STM32接口FSMC/FMC难点详解 转载 http://blog.sina.com.cn/s/blog_808bca130102x94k.html STM32F767的FMC将外部存储器划分为 ...
- spring2.0和spring2.5及以上版本的jar包区别 spring jar 包详解
spring jar 包详解 spring.jar是包含有完整发布的单个jar包,spring.jar中包含除了 spring-mock.jar里所包含的内容外其它所有jar包的内容,因为只有在开发环 ...
- java axis2 jar_Java axis2.jar包详解及缺少jar包错误分析
Java axis2.jar包详解及缺少jar包错误分析 一.最小开发jar集 axis2 开发最小jar包集: activation-1.1.jar axiom-api-1.2.13.jar ax ...
- Spring Boot的每个模块包详解
Spring Boot的每个模块包详解,具体如下: 1.spring-boot-starter 这是Spring Boot的核心启动器,包含了自动配置.日志和YAML. 2.spring-boot-s ...
最新文章
- uplay服务器未响应,《看门狗:军团》存在崩溃丢失存档BUG 育碧正在修复
- 数据挖掘分析的必要性
- vs2017 不能加载.vdproj
- ESP8266编译脚本
- 网络安全比赛理论答题(一)
- 覆盖所有类型的javascript深度克隆
- 星益云聚合收银台v1.45开源完整版【最终版本】
- layui 日期插件onchange事件失效的方法
- shell脚本实现Fibonacci数列
- 【令人头秃的线段树】线段树入门题目详解(代码逐句分析)
- redis设计秒杀活动图解
- Tegra environment
- building sasl.wrapper extention
- OpenCV/kornia/Pillow/Halcon/NI Vision/MIL/*计算机视觉资料汇总
- 计算机c盘能分区吗,电脑C盘还可以分盘吗?
- 自问自答学ArrayList,看这篇就够了,详解问答
- mac下sourcetree设置代理
- DirectShow 下载
- 厦门故事(三):枫叶随风飘落,重重地摔在了地面上
- 网站备案:阿里云-信息系统安全等级保护备案证明及网站服务合同下载