Renyddd Site
Created: 15 Nov 2022

Golang-error-type

演示代码

先模拟一个在写业务代码时常见的方法 GetDatabase ,其会根据 flag 参数决定是否返回数据或空指针和 error。 接着的 Client 函数代表一些常规的业务处理逻辑,如当你指定的查询层级小于数据库值时返回 internalError,或者层级小于等于一触发某网络故障。

package main

import (
        "errors"
        "fmt"
        "os"
)

var (
        ThirdPartyError = errors.New("third party error")
        InternalError   = errors.New("internal error")
)

func main() {
        data, err := Client(100)
        handleError(err)
        echoData(data)

        data, err = Client(0)
        handleError(err)
        echoData(data)

        data, err = Client(5)
        handleError(err)
        echoData(data)

}

func GetDatabase(errorFlag bool) (*int, error) {
        if errorFlag {
                return nil, fmt.Errorf("connection reset by peer")
        }
        var res = 10
        return &res, nil
}

func Client(level int) (int, error) {
        flag := false
        if level <= 1 {
                flag = true
        }

        ptr, err := GetDatabase(flag)
        if err != nil {
                hint := err.Error()
                return 0, fmt.Errorf("%s: %w", hint, ThirdPartyError)
        }

        if level < *ptr {
                return 0, fmt.Errorf("data is less than 10 %w", InternalError)
        }

        return *ptr, nil
}

func handleError(err error) {
        if err == nil {
                return
        }

        if errors.Is(err, ThirdPartyError) {
                fmt.Printf("match cmdb error: %v\n", err)
                return
        }
        if errors.Is(err, InternalError) {
                fmt.Printf("match internal error: %v\n", err)
                os.Exit(1)
        }
}

func echoData(d int) {
        fmt.Printf("got data: %d\n", d)
}

创建故障变量

errors.New(string) 根据指定参数创建,即返回了一个实现了 error interface 的对象。 注意,多次调用产生的都是不同变量,故多次即为不同值。

// New returns an error that formats as the given text.
// Each call to New returns a distinct error value even if the text is identical.
func New(text string) error {
        return &errorString{text}
}

// errorString is a trivial implementation of error.
type errorString struct {
        s string
}

func (e *errorString) Error() string {
        return e.s
}

故障包装

当在客户端调用访问数据库接口后,会结合方法返回值和业务逻辑通过 fmt.Errorf()%w 动词来进行包装:

  1. 方法出现如网络错误时 fmt.Errorf("%s: %w", hint, ThirdPartyError) 包装三方错误;
  2. 与内部逻辑冲突时,包装 InternalError 告知上游函数;

解包装

先想想一常见情景,当不使用 errors 包时,我们可区分外部或内部故障的方法有匹配 err.Error() 字符串,但该匹配字符串的方法耦合程度较高。

而使用 errors.Is 可无需过分关注 err string 成员值, Is 会在当前 err 的包装链上逐个查找。

// Is reports whether any error in err's chain matches target.
//
// The chain consists of err itself followed by the sequence of errors obtained by
// repeatedly calling Unwrap.
func Is(err, target error) bool
Creative Commons License
renyddd by Renyddd is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.