hello云胜

技术与生活

0%

go函数的多返回值与错误处理

在go的函数那片文章中,我们已经了解go函数的多返回值是go区别于其他静态编程语言的重要特征。

go语言的错误处理机制也是建立在函数多返回值的基础之上的。

借助多返回值的能力,可以将状态和数据分离,分别放在不同的返回值中。

一般go中的惯用法是,返回值的前几个为数据,最后一个是error类型表示是否出错,如果error是nil,表示没有错误。

error接口

error是go原生内置的接口类型

1
2
3
4
5
// The error built-in interface type is the conventional interface for
// representing an error condition, with the nil value representing no error.
type error interface {
Error() string
}

通常我们使用errors.New或者方便的构造一个error

1
2
err := errors.New("错误原因")
err2 := fmt.Errorf("fmt error")

这种用法很方便,但是错误信息只能是字符串。

如果我们需要更复杂的错误信息,就需要自定义error

比如json包中的一个自定义错误

1
2
3
4
5
6
7
type UnmarshalTypeError struct {
Value string
Type reflect.Type
Offset int64
Struct string
Field string
}

错误处理分支判断

在开发中经常遇到的情况是根据不同的错误原因走不通的分支处理。可能会写出这样的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
data, err := b.someFunc(1)
if err != nil {
switch err.Error() {
case "xxx: xxx原因":
// ... ...
return
case "xx: yy原因":
// ... ...
return
default:
// ... ...
return
}
}

这种写法严重依赖错误的返回信息,产生了代码的强耦合。一旦方法稍微改了下错误的原因,代码就会出错。

从 Go 1.13 版本开始,标准库 errors 包提供了 Is 函数用于判断错误值的类型

1
2
3
4
5
6
7
8
var ErrNotExist = errors.New("the resource not exist")

func TestIs(t *testing.T) {
testErr := fmt.Errorf("notExist,%w", ErrNotExist)
if errors.Is(testErr, ErrNotExist) {
t.Log(testErr.Error())
}
}

将错误作为一个导出错误值。方法调用方使用errors.Is进行判断

或者通过自定义Error的错误类型进行判读

从 Go 1.13 版本开始,标准库 errors 包提供了As函数,判断一个 error 类型变量是否为特定的自定义错误类型

1
2
3
4
5
6
7
8
9
10
11
12
13
type MyError struct {
e string
}
func (e *MyError) Error() string {
return e.e
}
func TestAs(t *testing.T) {
var err = &MyError{"MyError error demo"}
var e *MyError
if errors.As(err, &e) {
println("errinfo:", e.Error())
}
}

errors.As(err, &e)的作用是判断err是否是MyError类型,如果是,赋值给e