错误处理

Go语言错误处理的设计,虽然不能说完全是个垃圾,但也是相当欠考虑的。Go语言鼓励使用显式的错误检查和处理方式,它并没有一个像Java一样特别完善的异常处理机制,还是使用类似C语言中采用返回值和if进行判断,这种设计尽管在可读性和可维护性方面没有太大问题,但却让我们的代码丑陋无比。这篇笔记我们介绍Go语言中的错误处理机制。

打印和抛出异常类型

error接口

Go语言提供了error接口,用于表示异常信息,其定义如下:

type error interface {
    Error() string
}

所有实现了Error()方法的类型都被视为异常类型,常见的错误处理模式是返回一个error类型的值来表示是否发生了错误。下面例子调用了strconv.Atoi(),如果转换成功,errnil,否则表示转换出错了,我们可以调用Error()方法输出其信息。

package main

import (
    "fmt"
    "strconv"
)

func main() {
    i, err := strconv.Atoi("a")
    if err != nil {
        // 如果出错,打印错误消息,并中断处理流程
        fmt.Println("转换出错:", err.Error())
        return
    }

    fmt.Println(i)
}

当然,这里为了简单起见我们的代码中对于异常的处理就是使用fmt.Println()函数打印,并使用return中断函数的执行,实际开发中我们可能还需要编写复杂的异常处理和恢复逻辑。此外,使用fmt.Println()函数打印消息也不是个好的方式,实际开发中我们通常更倾向于使用日志库来输出错误信息,这里就不多介绍了。

抛出异常

我们自己定义的函数或方法内也可以通过返回值的形式指定抛出异常,调用处也可以通过这个返回值接收异常。下面是一个例子。

package main

import (
    "errors"
    "fmt"
)

type Person struct {
    name string
    age  int
}

func (p *Person) SayHello() (string, error) {
    if p.name == "" && p.age == 0 {
        return "", errors.New("未初始化!")
    } else {
        return fmt.Sprintf("Hello, I am %v, %v years old.", p.name, p.age), nil
    }
}

func main() {
    p := &Person{}
    s, err := p.SayHello()
    if err != nil {
        fmt.Println(err.Error())
    } else {
        fmt.Println(s)
    }
}

代码中,errors.New()方法能将一个字符串消息包装成error类型,我们调用处的代码可以用err.Error()取出该消息。

使用panic和recover

虽然Go不鼓励使用panicrecover进行一般的错误处理,但在某些情况下例如不可恢复的错误,我们可以使用它们。panic会主动触发程序“崩溃”,而recover可以捕获到这个崩溃并进行处理,下面是一个例子。

package main

import "fmt"

func main() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered from panic:", r)
        }
    }()
    panic("something went wrong")
}

recover通常结合defer使用。defer用于延迟执行某个函数,在defer后面的函数调用会在panic发生后被调用,而recover则用于捕获panic并恢复程序的执行。

当一个Goroutine发生panic并且没有recover来捕获时,该Goroutine将停止执行,并打印出一个栈跟踪信息。这些信息包括panic的消息以及导致panic的函数调用堆栈。如果panic发生在子Goroutine中,且没有被子Goroutine中的recover捕获,panic不会直接传播到父Goroutine。父Goroutine会继续执行,除非父Goroutine依赖子Goroutine的结果并访问一个未能正确初始化的共享资源;如果panic发生在主Goroutine中,并且没有被recover捕获,整个程序将崩溃,并打印出panic消息和栈跟踪信息。

作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。