返回顶部
首页 > 资讯 > 后端开发 > GO >Golang中的错误处理的示例详解
  • 671
分享到

Golang中的错误处理的示例详解

2024-04-02 19:04:59 671人浏览 薄情痞子
摘要

目录1、panic2、包装错误3、错误类型判断4、错误值判断1、panic 当我们执行panic的时候会结束下面的流程: package main import "fmt" fu

1、panic

当我们执行panic的时候会结束下面的流程:

package main

import "fmt"

func main() {
	fmt.Println("hello")
	panic("stop")
	fmt.Println("world")
}

输出:

Go run 9.go 
hello
panic: stop

但是panic也是可以捕获的,我们可以使用defer和recover实现:

package main

import "fmt"

func main() {

	defer func() {
		if r := recover(); r != nil {
			fmt.Println("recover: ", r)
		}
	}()

	fmt.Println("hello")
	panic("stop")
	fmt.Println("world")
}

输出:

go run 9.go
hello
recover:  stop

那什么时候适合panic呢?在 Go 中,panic 用于表示真正的异常,例如程序错误。我们经常会在一些内置包里面看到panic的身影。

比如strings.Repeat重复返回一个由字符串 s 的计数副本组成的新字符串:

func Repeat(s string, count int) string {
	if count == 0 {
		return ""
	}

	// 
	if count < 0 {
		panic("strings: negative Repeat count")
	} else if len(s)*count/count != len(s) {
		panic("strings: Repeat count causes overflow")
	}

	...
}

我们可以看到当重复的次数小于0或者重复count次之后s的长度溢出,程序会直接panic,而不是返回错误。这时因为strings包限制了error的使用,所以在程序错误时会直接panic。

还有一个例子是关于正则表达式的例子:

package main

import (
	"fmt"
	"regexp"
)

func main() {
	pattern := "a[a-z]b*" // 1
	compile, err := regexp.Compile(pattern) // 2
	if err != nil { // 2
		fmt.Println("compile err: ", err)
		return
	}
  // 3
	allString := compile.FindAllString("acbcdadb", 3)
	fmt.Println(allString)

}
  • 编写一个正则表达式
  • 调用Compile,解析正则表达式,如果成功,返回用于匹配文本的 Regexp 对象。否则返回错误
  • 利用正则,在输入的字符串中,获取所有的匹配字符

可以看到如果上面正则解析失败是可以继续往下执行的,但是regexp包中还有另外一个方法MustCompile:

func MustCompile(str string) *Regexp {
	regexp, err := Compile(str)
	if err != nil {
		panic(`regexp: Compile(` + quote(str) + `): ` + err.Error())
	}
	return regexp
}

这个方法说明正则的解析是强依赖的,如果解析错误,直接panic结束程序。用户可以根据实际情况选择。

但是实际开发中我们还是要谨慎使用panic,因为它会使程序结束运行(除非我们调用defer recover)

2、包装错误

错误包装是将错误包装或者打包在一个包装容器中,这样的话我们就可以追溯到源错误。错误包装的主要作用就是:

  • 为错误添加上下文
  • 将错误标记为特定类型的错误

我们可以看一个访问数据库的例子:

package main

import (
	"fmt"
	"GitHub.com/pkg/errors"
)

type Courseware struct {
	Id int64
	Code string
	Name string
}

func getCourseware(id int64) (*Courseware, error) {
	courseware, err := getFromDB(id)
	if err != nil {
		return nil, errors.Wrap(err, "六月的想访问这个课件") // 2
	}
	return courseware, nil
}

func getFromDB(id int64) (*Courseware, error) {
	return nil, errors.New("permission denied") // 1
}

func main() {
	_, err := getCourseware(11)
	if err != nil {
		fmt.Println(err)
	}
}
  • 访问数据库时我们返回了原始的错误信息
  • 到上层我们添加了一些自定义的上下文信息

输出:

go run 9.go
六月的想访问这个课件: permission denied

当然我们也可以将错误包装成我们自定义类型的错误,我们稍微修改下上面的例子:

package main

import (
	"fmt"
	"github.com/pkg/errors"
)

type Courseware struct {
	Id int64
	Code string
	Name string
}

// 1
type ForbiddenError struct {
	Err error
}

// 2
func (e *ForbiddenError) Error() string {
	return "Forbidden: " + e.Err.Error()
}

func getCourseware(id int64) (*Courseware, error) {
	courseware, err := getFromDB(id)
	if err != nil {
		return nil, &ForbiddenError{err} // 4
	}
	return courseware, nil
}

func getFromDB(id int64) (*Courseware, error) {
	return nil, errors.New("permission denied") // 3
}

func main() {
	_, err := getCourseware(11)
	if err != nil {
		fmt.Println(err)
	}
}
  • 首先我们自定义了ForbiddenError的错误类型
  • 我们实现了error接口
  • 访问数据库抛出原始错误
  • 上层返回ForbiddenError类型的错误

输出:

go run 9.go
Forbidden: permission denied

当然我们也可以不用创建自定义错误的类型,去包装错误添加上下文:

package main

import (
	"fmt"
	"github.com/pkg/errors"
)

type Courseware struct {
	Id int64
	Code string
	Name string
}


func getCourseware(id int64) (*Courseware, error) {
	courseware, err := getFromDB(id)
	if err != nil {
		return nil, fmt.Errorf("another wrap err: %w", err) // 1
	}
	return courseware, nil
}

func getFromDB(id int64) (*Courseware, error) {
	return nil, errors.New("permission denied")
}

func main() {
	_, err := getCourseware(11)
	if err != nil {
		fmt.Println(err)
	}
}

使用%w包装错误

使用这的好处是我们可以追溯到源错误,从而方便我们做一些特殊的处理。

还有一种方式是使用:

return nil, fmt.Errorf("another wrap err: %v", err)

%v的方式不会包装错误,所以无法追溯到源错误,但往往有时候我们会选择这种方式,而不用%w的方式。%w的方式虽然能包装源错误,但往往我们会通过源错误去做一些处理,假如源错误被修改,那包装这个源错误的相关错误都需要做响应变化。

3、错误类型判断

我们扩展一下上面查询课件的例子。现在我们有这样的判断,如果传进来的id不合法我们返回400错误,如果查询数据库报错我们返回500错误,我们可以像下面这样写:

package main

import (
	"fmt"
	"github.com/pkg/errors"
)

type Courseware struct {
	Id int64
	Code string
	Name string
}

type ForbiddenError struct {
	Err error
}

func (e *ForbiddenError) Error() string {
	return "Forbidden: " + e.Err.Error()
}

func getCourseware(id int64) (*Courseware, error) {
	if id <= 0 {
		return nil, fmt.Errorf("invalid id: %d", id)
	}
	courseware, err := getFromDB(id)
	if err != nil {
		return nil, &ForbiddenError{err}
	}
	return courseware, nil
}

func getFromDB(id int64) (*Courseware, error) {
	return nil, errors.New("permission denied")
}

func main() {
	_, err := getCourseware(500) // 我们可以修改这里的id看下打印的结构
	if err != nil {
		switch err := err.(type) {
		case *ForbiddenError:
			fmt.Println("500 err: ", err)
		default:
			fmt.Println("400 err: ", err)
		}
	}
}

输出:

go run 9.go
500 err:  Forbidden: permission denied

这样看起来好像也没什么问题,现在我们稍微修改下代码,把上面ForbiddenError包装一下:

package main

import (
	"fmt"
	"github.com/pkg/errors"
)

type Courseware struct {
	Id int64
	Code string
	Name string
}

type ForbiddenError struct {
	Err error
}

func (e *ForbiddenError) Error() string {
	return "Forbidden: " + e.Err.Error()
}

func getCourseware(id int64) (*Courseware, error) {
	if id <= 0 {
		return nil, fmt.Errorf("invalid id: %d", id)
	}
	courseware, err := getFromDB(id)
	if err != nil {
		return nil, fmt.Errorf("wrap err: %w", &ForbiddenError{err}) // 这里包装了一层错误
	}
	return courseware, nil
}

func getFromDB(id int64) (*Courseware, error) {
	return nil, errors.New("permission denied")
}

func main() {
	_, err := getCourseware(500)
	if err != nil {
		switch err := err.(type) {
		case *ForbiddenError:
			fmt.Println("500 err: ", err)
		default:
			fmt.Println("400 err: ", err)
		}
	}
}

输出:

go run 9.go
400 err:  wrap err: Forbidden: permission denied

可以看到我们的Forbidden错误进到了400里面,这并不是我们想要的结果。之所以会这样,是因为在ForbiddenError的外面又包装了一层Error错误,使用类型断言的时候判断出来的是Error错误,所以进到了400分支。

这里我们可以使用errors.As方法,它会递归调用Unwrap方法,找到错误链中第一个与target匹配的方法:

package main

import (
	"fmt"
	"github.com/pkg/errors"
)

type Courseware struct {
	Id int64
	Code string
	Name string
}

type ForbiddenError struct {
	Err error
}

func (e *ForbiddenError) Error() string {
	return "Forbidden: " + e.Err.Error()
}

func getCourseware(id int64) (*Courseware, error) {
	if id <= 0 {
		return nil, fmt.Errorf("invalid id: %d", id)
	}
	courseware, err := getFromDB(id)
	if err != nil {
		return nil, fmt.Errorf("wrap err: %w", &ForbiddenError{err})
	}
	return courseware, nil
}

func getFromDB(id int64) (*Courseware, error) {
	return nil, errors.New("permission denied")
}

func main() {
	_, err := getCourseware(500)
	if err != nil {
		var f *ForbiddenError // 这里实现了*ForbiddenError接口,不然会panic
		if errors.As(err, &f) { // 找到匹配的错误
			fmt.Println("500 err: ", err)
		} else {
			fmt.Println("400 err: ", err)
		}
	}
}

输出:

go run 9.go
500 err:  wrap err: Forbidden: permission denied

4、错误值判断

在代码中或者Mysql库或者io库中我们经常会看到这样的全局错误:

var ErrCourseware = errors.New("courseware")

这种错误我们称之为哨兵错误。一般数据库没查到ErrNoRows或者io读到了EOF错误,这些特定的错误可以帮助我们做一些特殊的处理。

一般我们会直接用==号判断错误值,但是就像上面的如果错误被包装哪我们就不好去判断了。好在errors包中提供了errors.Is方法,通过递归调用Unwrap判断错误链中是否与目标错误相匹配的错误值:

if err != nil {
    if errors.Is(err, ErrCourseware) {
        // ...
    } else {
        // ...
    }
}

到此这篇关于golang中的错误处理的示例详解的文章就介绍到这了,更多相关Golang错误处理内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

您可能感兴趣的文档:

--结束END--

本文标题: Golang中的错误处理的示例详解

本文链接: https://lsjlt.com/news/121291.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Golang中的错误处理的示例详解
    目录1、panic2、包装错误3、错误类型判断4、错误值判断1、panic 当我们执行panic的时候会结束下面的流程: package main import "fmt" fu...
    99+
    2024-04-02
  • golang函数中错误处理机制详解
    golang 函数中错误处理机制利用 error 类型和 error 标准接口来处理错误。错误类型可自定义,通过 error() 方法返回错误描述。错误处理机制包括显式错误(通过返回值传...
    99+
    2024-05-03
    golang 错误处理
  • 深入理解golang函数中的错误处理
    go 语言中的错误处理机制允许您优雅地处理错误,避免应用程序崩溃。错误类型为 error 接口,包含错误消息字符串。错误处理语法包括:err 变量接收错误,if err != nil 块...
    99+
    2024-05-04
    golang 错误处理
  • Node错误处理的示例分析
    小编给大家分享一下Node错误处理的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!01 Error JS 中的...
    99+
    2024-04-02
  • Golang错误处理计划:错误类型分类及处理技巧详解
    Golang 错误处理计划:错误类型分类及处理技巧详解 引言: 错误处理是编程中一个至关重要的方面,它帮助我们在程序出现异常情况时做出及时的响应和处理。在 Golang 中,错误处理被...
    99+
    2024-03-09
    技巧 golang 错误处理
  • golang函数错误处理中的异步处理
    在 go 函数中,异步错误处理通过使用 error 通道,异步地从 goroutine 传递错误。具体步骤如下:创建一个 error 通道。启动一个 goroutine 来执行操作并异步...
    99+
    2024-05-03
    错误处理 异步处理 golang 并发请求
  • 如何处理Golang的错误
    Golang是一种强类型、面向对象的开发语言,因其高效性和并发性而备受推崇。在使用Golang开发过程中,经常会遇到返回错误的情况,因此我们需要学会如何处理Golang的错误。Golang中的错误类型在Golang中,错误类型属于内置类型之...
    99+
    2023-05-14
  • golang函数的错误处理
    go 中的错误处理通过 error 类型实现,提供 error() 方法返回错误信息。有两种错误处理方法:显式检查:使用 if 语句检查错误并进行处理。错误传播:将错误传递给调用方函数,...
    99+
    2024-04-20
    golang 错误处理
  • NodeJS处理Express中异步错误的示例分析
    小编给大家分享一下NodeJS处理Express中异步错误的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!Express...
    99+
    2024-04-02
  • Golang中的错误处理深入分析
    目录一、Go的内建类型error二、怎么判断一个错误值具体代表那一类错误知道错误类型的所属范围知道错误变量是哪几个值三、错误值体系的两种方法立体的-错误类型体系扁平的-错误值列表一、...
    99+
    2023-01-09
    Go错误处理 GoLang错误处理
  • Golang中错误处理的优秀方法
    错误处理在编程中是一个非常重要的主题。在Golang中,错误处理的方式与其他编程语言有所不同,因此需要掌握一些最佳实践来优化代码的可读性和可维护性。 1. 返回错误值而不是抛出异常 与...
    99+
    2024-02-26
    实践 golang 错误处理
  • 如何处理 Golang 中的 nil 错误值?
    在 go 中处理 nil 错误值有以下方法:显式检查错误,例如 if err == nil。使用 errors.is 和 errors.as 函数进行错误比较和类型转换。使用特定错误类型...
    99+
    2024-05-14
    golang nil错误值
  • golang函数中的错误处理技巧
    golang 函数中的错误处理技巧:使用 error.error() 将错误转换为字符串。使用 printf 格式化错误消息。使用 wrap 添加错误摘要。定义自定义错误类型以捕获重复错...
    99+
    2024-05-04
    golang 错误处理
  • golang gorm错误处理事务以及日志用法示例
    目录1. 高级用法1.1. 错误处理1.2. 事物1.2.1. 一个具体的例子1.3. SQL构建1.3.1. 执行原生SQL1.3.2. sql.Row & sql.Row...
    99+
    2024-04-02
  • Golang中的错误处理方式有哪些
    这篇文章主要讲解了“Golang中的错误处理方式有哪些”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Golang中的错误处理方式有哪些”吧!错误类型在Golang中,错误类型是错误处理的基本...
    99+
    2023-07-06
  • Golang函数中错误的传播和处理
    go 函数中,函数可以通过返回 error 对象传播错误,调用函数负责处理。错误处理方式包括:忽略错误、日志记录、告警和返回错误。实战中,可以使用错误处理器轻松处理可能发生的错误,并使用...
    99+
    2024-04-24
    golang 错误处理
  • 如何自定义处理 Golang 中的错误?
    自定义处理 golang 错误的方法为:创建自定义错误类型,实现 error.error 接口。在实战案例中,使用自定义错误处理程序以更具体地指定错误信息,便于调试和处理。 如何自定义...
    99+
    2024-05-15
    golang 错误处理
  • golang函数错误处理中的国际化
    golang 函数可以通过 errors 包中的 wrapf 和 errorf 函数进行错误国际化,从而创建本地化的错误消息,并附加到其他错误中,形成更高级别的错误。通过使用 wrapf...
    99+
    2024-05-05
    错误处理 国际化 golang
  • 深入理解golang函数的错误处理机制
    在 go 中,函数通过 error 变量表示错误,这是一个实现了 error() 方法的接口。如果函数成功执行,则 error 变量应为 nil。而要将错误从函数中传播出来,需要在函数签...
    99+
    2024-04-24
    golang 错误处理 标准库
  • golang函数中的错误处理与异常处理的比较
    go 语言提供两种错误处理机制:1. 错误处理:使用 err 参数返回错误,调用方需显式处理;2. 异常处理:使用 panic() 和 recover() 函数,引发并捕获异常。实战场景...
    99+
    2024-04-25
    错误处理 异常处理 golang
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作