10.17. error

Go语言不像其他编程语言一样可以对错误或者异常经常进行捕获操作,所以代码中经常会包含对错误信息的处理逻辑。在项目中如何处理错误类型,尤其是在大型项目中如何对错误信息进行处理呢?

10.17.1. 1.示例

package main

import (
    "errors"
    "fmt"
)

func ErrorUsage() {

    err := errors.New("err: found 1")
    if err != nil {
        fmt.Println(err.Error())    // err: found 1
    }
    err2 := fmt.Errorf("err: %s", "found 2")
    if err2 != nil {
        fmt.Println(err2.Error())   // err: found 2
    }
}

func main() {
    ErrorUsage()
}

要实现错误类型,有errors.New和fmt.Errorf两种方式,它们实质上是一样的,一种带格式,另一种不带格式,底层其实是一样的。

func Errorf(format string, a ...interface{}) error {
    p := newPrinter()
    p.wrapErrs = true
    p.doPrintf(format, a)
    s := string(p.buf)
    var err error
    if p.wrappedErr == nil {
        err = errors.New(s)
    } else {
        err = &wrapError{s, p.wrappedErr}
    }
    p.free()
    return err
}


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
}

实际上,errors.New实例化的errorString实现了内置的error接口。

type error interface {
    Error() string
}

如果想要自定义项目的错误类型,那么实现error接口即可。

package main

import "fmt"

type SelfError struct {
    Code    int    `json:"code"`
    Message string `json:"message"`
}

func (self SelfError) Error() string {
    return fmt.Sprintf("Code: %d, Message: %s", self.Code, self.Message)
}

func UsageError(value string) error {
    var self SelfError
    if value == "" {
        self.Code = 400
        self.Message = "fail"
        return self
    }
    return nil
}

func UserErrorUsage() {

    err3 := UsageError("")
    if err3 != nil {
        fmt.Println(err3.Error())
    }

}

func main() {
    UserErrorUsage()    // Code: 400, Message: fail
}

10.17.2. 2.速记

import "errors"

errors包实现了创建错误值的函数。

使用字符串创建一个错误,请类比fmt包的Errorf方法,差不多可以认为是New(fmt.Sprintf(…))。

err := errors.New("emit macho dwarf: elf header corrupted")
if err != nil {
    fmt.Print(err)    //emit macho dwarf: elf header corrupted
}
const name, id = "bimmler", 17
err := fmt.Errorf("user %q (id %d) not found", name, id)
if err != nil {
    fmt.Print(err)    //user "bimmler" (id 17) not found
}

如果读者经常阅读源代码,那么应该能从中发现一般的库是如何处理错误信息的。

库的作用是解决某一类的问题,比如字符串处理、排序等,所以整体上和企业级的错误处理方式不太一样,一般开发者的思路是在库的起始位置定义一些频繁使用的错误类型。

真实的企业项目一般是自定义一个结构体,定义好其中的字段,而后单独成为项目的error库(或程序包),该结构体实现了内置error接口。

单独成为一个库的好处是能够在项目中多处复用。