错误处理策略
- 隐藏内部细节直接返回错误:上层不清楚错误是在哪一层导致的,只是知道了错误
- 返回和检查错误值: 通过返回特定值表示是否成功或具体的错误,有点类似linux中的错误码处理
- 自定义错误类型:逻辑中增加类型断言,断言成功就是具体的错误
隐藏内部细节直接返回错误
conn, err := net.Dial("tcp", "golang.org:80")
if err != nil {
return err
}
返回和检查错误值
fp, err := os.Open("./xxx.txt")
if err != nil {
fmt.Println("打开文件失败。", err)
return
}
defer fp.Close()
for {
n, err2 := fp.Read(buf)
if err2 == io.EOF { // io.EOF表示文件末尾
fmt.Println("文件读取结束")
break
}
fmt.Print(string(buf[:n]))
}
自定义错误类型
type MyError struct {
error errors.error
Message string
}
func (e *MyError) Error() string {
return fmt.Sprintf("message:%s error=%s", e.Message, errors.Error())
}
func test() *MyError{
return &MyError{Message:"xxx", error:errors.New("error")}
}
func main() {
err := test()
if _,ok := err.(*MyError);ok {
//错误处理
}
}
比较出名的errors包
因为golang errors比较“简洁”,所以这个时候出现了一些解决痛点的包
比较出名的是
https://github.com/pkg/errors
可以输出具体的堆栈
package main
import (
"fmt"
"github.com/pkg/errors"
)
func test() error {
cause := errors.New("test")
return errors.WithStack(cause)
}
func main() {
err := test()
fmt.Printf("%+v", err)
}
输出
test
main.test
/Users/timi/go/src/test/test.go:1485
main.main
/Users/timi/go/src/test/test.go:1490
runtime.main
/usr/local/go/src/runtime/proc.go:203
runtime.goexit
/usr/local/go/src/runtime/asm_amd64.s:1357
main.test
/Users/timi/go/src/test/test.go:1486
main.main
/Users/timi/go/src/test/test.go:1490
runtime.main
/usr/local/go/src/runtime/proc.go:203
runtime.goexit
/usr/local/go/src/runtime/asm_amd64.s:1357%
多层嵌套错误
import (
"fmt"
"github.com/pkg/errors"
)
func error1() error {
return errors.New("error1")
}
func inner() error {
err := error1()
if err != nil {
return errors.Wrap(err, "inner")
}
return nil
}
func middle() error {
err := inner()
if err != nil {
return errors.Wrap(err, "middle")
}
return nil
}
func outer() error {
err := middle()
if err != nil {
return errors.Wrap(err, "outer")
}
return nil
}
func main() {
err := outer()
fmt.Printf("%+v\n", err)
`fmt.Println(errors.Cause(err))
}
输出
error1
main.error1
/Users/timi/go/src/test/test.go:1485
main.inner
/Users/timi/go/src/test/test.go:1489
main.middle
/Users/timi/go/src/test/test.go:1497
main.outer
/Users/timi/go/src/test/test.go:1504
main.main
/Users/timi/go/src/test/test.go:1512
runtime.main
/usr/local/go/src/runtime/proc.go:203
runtime.goexit
/usr/local/go/src/runtime/asm_amd64.s:1357
inner
main.inner
/Users/timi/go/src/test/test.go:1491
main.middle
/Users/timi/go/src/test/test.go:1497
main.outer
/Users/timi/go/src/test/test.go:1504
main.main
/Users/timi/go/src/test/test.go:1512
runtime.main
/usr/local/go/src/runtime/proc.go:203
runtime.goexit
/usr/local/go/src/runtime/asm_amd64.s:1357
middle
main.middle
/Users/timi/go/src/test/test.go:1499
main.outer
/Users/timi/go/src/test/test.go:1504
main.main
/Users/timi/go/src/test/test.go:1512
runtime.main
/usr/local/go/src/runtime/proc.go:203
runtime.goexit
/usr/local/go/src/runtime/asm_amd64.s:1357
outer
main.outer
/Users/timi/go/src/test/test.go:1506
main.main
/Users/timi/go/src/test/test.go:1512
runtime.main
/usr/local/go/src/runtime/proc.go:203
runtime.goexit
/usr/local/go/src/runtime/asm_amd64.s:1357
++++++++
outer: middle: inner: error1
golang1.13 官方标准包errors新特性
主要增加了几个函数
fmt.Errorf//包装 每次调用包一次
errors.Unwrap//解包装 每次调用解一次
errors.Is
errors.As
单讲语法没什么意义
主要将以前写法和现在写法的区别
这里有个需要注意的点,就是1.13开始支持错误链,类似上面实现的那样 error1->inner->middle->outer
//错误链演示
import (
"errors"
"fmt"
)
var IamError error = errors.New("i am error1")
func error1() error {
return IamError
}
func inner() error {
err := error1()
if err != nil {
return fmt.Errorf("inner,%w", err)
}
return nil
}
func middle() error {
err := inner()
if err != nil {
return fmt.Errorf("middle,%w", err)
}
return nil
}
func outer() error {
err := middle()
if err != nil {
return fmt.Errorf("outer,%w", err)
}
return nil
}
func main() {
err := outer()
fmt.Println(err)
fmt.Println("++++++++")
fmt.Println(errors.Is(err, IamError))
}
输出
outer,middle,inner,i am error1
++++++++
true
if err == io.EOF//以前这么写判断
if errors.Is(err, io.EOF)//1.13可以这样写 只要错误链含有io.EOF就返回true
if _,ok := err.(*MyError);ok {//以前这么写判断
var myError *MyError//1.13可以这样写
errors.As(newErr, &myError)//1.13可以这样写 只要错误链含有myError就返回true