go语言标准错误error全面解析-kb88凯时官网登录

时间:2024-10-20
阅读:
免费资源网,https://freexyz.cn/

错误类型

  • errorstring

错误是程序中处理逻辑和系统稳定新的重要组成部分。

在go语言中内置错误如下:

// 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}

error类型是一个接口类型,内含一个error的方法。它是错误的顶级接口,实现了此内置方法的结构体都是其子类。

errorstring结构体是内置实现错误接口的内置实现,源码如下:

// 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
}

new方法是内置错误类型实现。

errors.new()是最常用的错误类实现方法。

  • wraperror

wraperror是error的另一种实现类,位于fmt的包中,源码如下:

// errorf formats according to a format specifier and returns the string as a
// value that satisfies error.
//
// if the format specifier includes a %w verb with an error operand,
// the returned error will implement an unwrap method returning the operand.
// if there is more than one %w verb, the returned error will implement an
// unwrap method returning a []error containing all the %w operands in the
// order they appear in the arguments.
// it is invalid to supply the %w verb with an operand that does not implement
// the error interface. the %w verb is otherwise a synonym for %v.
func errorf(format string, a ...any) error {
	p := newprinter()
	p.wraperrs = true
	p.doprintf(format, a)
	s := string(p.buf)
	var err error
	switch len(p.wrappederrs) {
	case 0:
		err = errors.new(s)
	case 1:
		w := &wraperror{msg: s}
		w.err, _ = a[p.wrappederrs[0]].(error)
		err = w
	default:
		if p.reordered {
			slices.sort(p.wrappederrs)
		}
		var errs []error
		for i, argnum := range p.wrappederrs {
			if i > 0 && p.wrappederrs[i-1] == argnum {
				continue
			}
			if e, ok := a[argnum].(error); ok {
				errs = append(errs, e)
			}
		}
		err = &wraperrors{s, errs}
	}
	p.free()
	return err
}
type wraperror struct {
	msg string
	err error
}
func (e *wraperror) error() string {
	return e.msg
}
func (e *wraperror) unwrap() error {
	return e.err
}

wraperror是另一个内置错误实现类,使用%w作为占位符,这里的wraperror实现了unwrap方法,用户返回内置的err即嵌套的err。

wraperror还有一个复数形式wraperrors这里不再过多赘述。

  • 自定义错误

实现自定义错误非常简单,面向对象的特性实现错误接口erros就是实现了错误类。安装go语言的继承的特性,实现接口对应的方法即可。

type error interface {
	error() string
}
type myerr struct {
	e string
}
func (s *myerr) error() string {
	return s.e
}
func main(){
	var testerr error
	testerr = &myerr{"err"}
	fmt.println(testerr.error())	
}

上述代码就是实现了一个自定义的error类型,注意它是一个结构体,实际上是errorstring的子类。

新建错误

上一小节介绍了三种错误类型,前两中是内置的错误类型,其中自定义的错误是可拓展的,可以实现前两种的任意一个。

第一种errorstring是实现比较方便,只有实现error()方法;

第二种是wraperror需要实现两种方法,还有一种是unwrap()

  • errors.new()
err := errors.new("this is a error")
fmt.printf("----%t----%v\n", err, err)

该方法创建的是errorstring类实例

  • fmt.errorf()
err = fmt.errorf("err is: %v", "no found")
fmt.println(err)

该方法创建的是wraperror类实例,wraperror也是errorstring的子类。

  • 实例化
type myerr struct {
	e string
}
func (s *myerr) error() string {
	return s.e
}
func main(){
	var testerr error
	testerr = &myerr{"err"}
	fmt.println(testerr.error())
}

由于自定义错误一般需要错误信息,所以一般直接构造方法实例化。

错误解析

  • errors.is()

errors.is 用于判断一个错误是否与另一个特定的错误相等。它不仅仅是简单的比较错误的值,还会检查错误链中的所有错误,看看它们是否与给定的目标错误匹配。

package main
import (
    "errors"
    "fmt"
)
var errnotfound = errors.new("not found")
func finditem(id int) error {
    if id == 0 {
        return errnotfound
    }
    return nil
}
func main() {
    err := finditem(0)
    if errors.is(err, errnotfound) {
        fmt.println("item not found")
    } else {
        fmt.println("item found")
    }
}

注意这里有一个坑,就是is方法判断的错误的类型和错误的信息,使用new方法即使构建的错误信息相同类型不一样也是不相等的,如下:

err1 := errors.new("err1")
err2 := errors.new("err1")
err := errors.is(err1, err2)
fmt.println(err) // 输出: false
  • errors.as()

errors.as 用于将一个错误转换为特定的错误类型。如果错误链中的某个错误匹配给定的目标类型,那么 errors.as 会将该错误转换为该类型,并将其赋值给目标变量。

package main
import (
    "errors"
    "fmt"
)
type myerror struct {
    code int
    msg  string
}
func (e *myerror) error() string {
    return fmt.sprintf("code %d: %s", e.code, e.msg)
}
func dosomething() error {
    return &myerror{code: 404, msg: "not found"}
}
func main() {
    err := dosomething()
    var myerr *myerror
    if errors.as(err, &myerr) {
        fmt.printf("custom error: %v (code: %d)\n", myerr.msg, myerr.code)
    } else {
        fmt.println("unknown error")
    }
}

可用作类型判断。

  • errors.unwrap()

errors.unwrap() 是一个用于处理嵌套或包装错误的函数。它的主要作用是提取并返回一个错误的直接底层错误(即被包装的错误),如果该错误没有被包装过,则返回 nil

package main
import (
	"errors"
	"fmt"
)
func main() {
	baseerr := errors.new("base error")
	wrappederr := fmt.errorf("wrapped error: %w", baseerr)
	// 使用 errors.unwrap 来提取底层错误
	unwrappederr := errors.unwrap(wrappederr)
	fmt.println("wrapped error:", wrappederr)
	fmt.println("unwrapped error:", unwrappederr)
}
// output
wrapped error: wrapped error: base error
unwrapped error: base error

该方法只能获取错误的直接内嵌错误,如果要获取更深层次的错误需要便利判断。

注意:

  • errors.is: 用于判断一个错误是否与特定的错误值相等。适合在错误链中查找某个特定的错误(比如一个已知的预定义错误)。
  • errors.as: 用于将错误转换为特定的错误类型。适合当你需要根据错误的具体类型来处理错误时使用。
  • errors.unwrap() 用于从一个包装错误中提取并返回底层的错误。如果错误没有被包装过(或者没有实现 unwrap 方法),它会返回 nil

错误处理

if err := findall(); err != nil {
	// logic
}

这个处理过程是不是很眼熟,利用错误解析小节的处理方法可以对错误判断,进行后续的处理。

go语言也提供了错误捕获recover的机制和错误抛出panic机制。

  • panic

panic 是 go 语言中的一种触发异常处理机制的方式。当你调用 panic 时,程序会立刻停止执行当前函数,并从调用栈中逐层向上抛出,直到找到一个适合的 recover,或者最终导致程序崩溃。

package main
import "fmt"
func main() {
    fmt.println("start")
    panic("something went wrong!")
    fmt.println("end") // this line will not be executed
}

当程序中调用了 panic,程序会立刻中止当前的控制流,开始回溯帧,并执行每一层的 defer 语句。在执行完所有的 defer 语句后,如果没有遇到 recover,程序将崩溃,并打印 panic 的信息和堆栈跟踪。

  • recover

recover 是一个内建函数,用于恢复 panic。它只能在 defer 函数中有效。当 panic 发生时,如果当前函数任何调用栈上的函数中有 defer 函数调用了 recover,那么可以捕获 panic 的内容,并使程序恢复正常执行。

package main
import "fmt"
func main() {
    defer func() {
        if r := recover(); r != nil {
            fmt.println("recovered from:", r)
        }
    }()
    fmt.println("start")
    panic("something went wrong!")
    fmt.println("end") // this line will not be executed
}

recover 通常用于确保某些代码块即使发生了 panic,也能执行资源清理操作,并避免整个程序崩溃。

  • defer

defer 语句用于延迟函数的执行,直到包含 defer 语句的函数执行完毕时,才会执行。这通常用于确保资源释放或清理操作(如关闭文件、解锁互斥锁等)即使在函数中发生错误或提前返回时也会被执行。

  • 执行顺序: 在一个函数中,你可以有多个 defer 语句。这些 defer 调用的执行顺序是后进先出(lifo)。也就是说,最后一个 defer 声明的语句会最先执行。
  • 捕获值: defer 语句会在声明时捕获它引用的变量的值。也就是说,defer 语句中的参数会在声明 defer 时计算,而不是在 defer 执行时计算。

三个函数组合构成了错误处理,如下:  

package main
import "fmt"
func main() {
    defer func() {
        if r := recover(); r != nil {
            fmt.println("recovered from panic:", r)
        }
    }()
    fmt.println("starting the program")
    causepanic()
    fmt.println("program ended gracefully")
}
func causepanic() {
    fmt.println("about to cause panic")
    panic("a severe error occurred")
    fmt.println("this line will not execute")
}
starting the program
about to cause panic
recovered from panic: a severe error occurred

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持。

免费资源网,https://freexyz.cn/
返回顶部
顶部
网站地图