Go编程模式 - 4.错误处理

简介: 如何Wrap Error,在多人协同开发、多模块开发过程中,很难统一。而一旦不统一,容易出现示例中的过度Unwrap的情况。

目录

Functional

type Number struct {
   
    a int
    b string
    c bool
    d []int32
    e error
}

func (n *Number) parse(r io.Reader) error {
   
    if err := binary.Read(r, binary.BigEndian, &n.a); err != nil {
   
        return err
    }
    if err := binary.Read(r, binary.BigEndian, &n.b); err != nil {
   
        return err
    }
    if err := binary.Read(r, binary.BigEndian, &n.c); err != nil {
   
        return err
    }
    if err := binary.Read(r, binary.BigEndian, &n.d); err != nil {
   
        return err
    }
    if err := binary.Read(r, binary.BigEndian, &n.e); err != nil {
   
        return err
    }
    return nil
}

引入了函数式编程的方式,我们看看有什么改变

func (n *Number) parse(r io.Reader) error {
   
    // 先定义一个error
    var err error

    // 定义函数,注意这里的err的作用域是来自上面定义的
    read := func(data interface{
   }) {
   
        // 先检查error,如果已经有错误则不检查
        if err != nil {
   
            return
        }
        err = binary.Read(r, binary.BigEndian, data)
    }

    // 注意,这个函数的调用逻辑和之前的差别在于一点:
    // 即使前面的发生了error,下面的函数也会被调用
    read(&n.a)
    read(&n.b)
    read(&n.c)
    read(&n.d)
    read(&n.e)

    return err
}

ErrorObject

先看一个标准库中的实现

func main() {
   
    input := bytes.NewReader([]byte("hello"))

    // 扫描数据,这里不会直接返回错误
    scanner := bufio.NewScanner(input)
    for scanner.Scan() {
   
        token := scanner.Text()
        fmt.Println(token)
    }

    // 从Err()方法中获取错误
    if err := scanner.Err(); err != nil {
   
        fmt.Println(err)
    }
}

它的根本思想,是将error嵌入到了对象中。那我们借鉴一下

type Reader struct {
   
    r   io.Reader
    err error
}


func (r *Reader) read(data interface{
   }) {
   
    if r.err == nil {
   
        r.err = binary.Read(r.r, binary.BigEndian, data)
    }
}

func (n *Number) parse(reader io.Reader) error {
   
    r := Reader{
   r: reader}

    r.read(&n.a)
    r.read(&n.b)
    r.read(&n.c)
    r.read(&n.d)
    r.read(&n.e)

    return r.err
}

捎带提一句:个人不太喜欢上面scanner的错误处理方式,这个要求使用方对这个包很熟悉,否则很容易忘掉后面的错误处理逻辑。但后面处理错误的逻辑,就很直接地将错误返回,可读性很强。

Wrap

耗子叔给的例子是调用了github.com/pkg/errors下的wrap包,不过我更倾向于直接用原生的。

func main() {
   
    // 原始 error
    err := errors.New("level 1")
    fmt.Println(err)
    // level 1

    // wrap一下error,注意error的占位符是%w
    wraped := fmt.Errorf("%v: %w", "level 2", err)
    fmt.Println(wraped)
    // level 2: level 1

    // unwrap 后获得原来的错误
    unwraped := errors.Unwrap(wraped)
    fmt.Println(unwraped)
    // level 1

    // 过度unwrap会导致错误变成nil
    unwraped2 := errors.Unwrap(unwraped)
    fmt.Println(unwraped2)
    // nil
}

但在实际项目实践中,Wrap的这个特性并不好用:

如何Wrap Error,在多人协同开发、多模块开发过程中,很难统一。而一旦不统一,容易出现示例中的过度Unwrap的情况。

所以,我认为与其花大精力在制定错误的标准上,还不如利用fmt.Errorf将错误信息直观地表述出来。

目录
相关文章
|
6月前
|
SQL 安全 数据库连接
《Go 简易速速上手小册》第6章:错误处理和测试(2024 最新版)(上)
《Go 简易速速上手小册》第6章:错误处理和测试(2024 最新版)
71 1
|
6月前
|
Go 开发者
Go语言中的错误处理与异常机制:实践与最佳策略
【2月更文挑战第7天】Go语言以其独特的错误处理机制而闻名,它鼓励显式错误检查而不是依赖于异常。本文将探讨错误处理与异常机制在Go语言中的实际应用,并分享一些最佳实践,帮助开发者编写更加健壮和易于维护的Go代码。
|
6月前
|
存储 Cloud Native 编译器
云原生系列Go语言篇-错误处理
从其它语言转Go最大的挑战之一就是错误处理。对那些习惯了使用异常的开发者,Go采取的方法有些离经叛道。但Go语言使用的方法蕴含着牢不可破的软件工程原理。本章中,我们学习如何在Go中处理错误。我们会学习Go系统中会停止执行的错误处理panic和recover。
72 0
|
6月前
|
JSON 测试技术 Go
《Go 简易速速上手小册》第6章:错误处理和测试(2024 最新版)(下)
《Go 简易速速上手小册》第6章:错误处理和测试(2024 最新版)
63 0
|
2月前
|
Go
Go to Learn Go之错误处理
Go to Learn Go之错误处理
44 7
|
3月前
|
Go 开发者
理解Go语言中的错误处理
【8月更文挑战第31天】
16 0
|
3月前
|
Java Go PHP
Go从入门到放弃之错误处理
Go从入门到放弃之错误处理
|
5月前
|
Go
go错误处理
go错误处理
|
5月前
|
Go
掌握Go语言:Go语言错误处理,从基础到高级,让你的代码更健壮!(33)
掌握Go语言:Go语言错误处理,从基础到高级,让你的代码更健壮!(33)
|
5月前
|
SQL 数据库连接 Go
掌握Go语言:Go语言精细错误,清晰、高效的错误处理实践(32)
掌握Go语言:Go语言精细错误,清晰、高效的错误处理实践(32)