首页 > 其他分享 >Go 语言入门很简单:Go 语言的错误处理

Go 语言入门很简单:Go 语言的错误处理

时间:2022-10-18 23:01:05浏览次数:67  
标签:errors return 语言 err 错误 fmt error Go 错误处理

Go 语言入门很简单:Go 语言的错误处理_异常处理

本文将介绍 Go 中的错误处理,以及为什么我们需要错误处理。

什么是错误处理

异常处理是任何语言都不能绕不开的话题。Go 语言没有提供传统的 ​​try...catch​​ 语句来处理异常,而是通过使用 ​​error​​ 来处理错误,用 ​​panic​​ 和 ​​recover​​ 来处理异常。

错误封装是将一个错误包裹到另一个错误的过程。假设我们有一个访问数据库的 Web 服务器,并试图从数据库中获取一条记录。如果数据库调用返回一个错误,我们可以决定是捕获这个错误还是从网络服务中发送我们自己的自定义错误。

基础 error

error 接口

error 是一种内建的接口类型,内建意味着不需要 ​​import​​ 任何包就可以直接使用,使用起来就像基础类型一样自然。

type error interface {
Error() string
}

error 接口只声明了一个 ​​Error()​​ 方法,任何实现了该方法的结构体都可以作为 error 来使用。

  • ​error​​ 的实例代表一种异常状态
  • ​Error()​​ 方法用于描述该异常状态
  • 值为​​nil​​ 的 error 代表没有异常

标准库 ​​errors​​ 包中的 ​​errorString​​ 就是实现 error 接口的一个例子:

type errorString struct {
s string
}

func (e *errorString) Error() string {
return.s
}

创建 error

标准库提供了两种 error 的方法:

  • ​errors.New()​
  • ​fmt.Errorf()​
  1. ​error.New()​​ 的实现极其简单,只是简单地构造一个​​errorString​​ 实例便返回:
package errors

func New(text string) error {
return &errorString{text}
}
  1. ​fmt.Errorf()​

​error.New()​​ 单调地接收一个字符串参数来构造 error,而实际场景中往往需要使用 ​​fmt.Sprintf()​​ 生成字符串,这时可以直接使用 ​​fmt.Errorf()​​:

package fmt

func Errorf(format string, a ...interface{}) error {
return errors.New(Sprintf(format, a...))

联系与区别: ​​fmt.Errorf()​​ 只是对 ​​error.New()​​ 的简单封装,使用前者可以使得代码更加简洁。而两者的区别在于 ​​fmt.Errorf()​​ 适用于需要格式化输出错误字符串的场景,如果不需要格式化字符串,则建议使用 ​​error.New()​​。

异常处理

针对 error 而言,异常处理包括如何检查错误,如何传递错误。

  1. 检查 error

最常见的检查 ​​error​​ 的方式是与 ​​nil​​ 值进行比较:

if err != nil {
// 错误处理逻辑
}

与预定义的 error 进行比较(可以是自定义也可以是标准库中的,比如 ​​os​​ 库中的常见错误):

var noRows = errors.New("no rows found")
if err == noRows {

}
  1. 传递 error

在一个函数中收到一个 error,往往需要附加一些上下文信息再把 error 继续向上层抛

可以看一下如下的例子:

package main

import (
"errors"
"fmt"
)

var noRows = errors.New("no rows found")

func getRecords() error {
return noRows
}

func webService() error {

if err := getRecords(); err != nil {
return fmt.Errorf("Error %s when calling DB", err)
}
return nil
}

func main() {
if err := webService(); err != nil {
fmt.Printf("Error: %s when calling webService\n", err)
return
}
fmt.Println("webService call successful")
}

运行结果:

$ go run .
Error: Error no rows found when calling DB when calling webService

在上面的程序中,我们在调用 ​​getRecords​​ 函数时,发送错误的字符串描述,虽然这可能看起来像错误处理,但实际上并不是。让我们接着看下去吧。

错误处理和 Is 函数

Go 语言中的 ​​errors​​ 包中的 ​​Is​​ 函数会判断目标是否有相应的错误匹配的上,在我们上一节的例子中,从 ​​getRecords​​ 函数中返回 ​​nowRows​​ 错误,然后这个错误的字符串信息从 ​​webService​​ 函数中返回,如果使用上 ​​Is​​ 函数,判断有没有查询到数据,然后才返回 ​​noRows​​ 错误:

package main

import (
"errors"
"fmt"
)

var noRows = errors.New("no rows found")

func getRecords() error {
return noRows
}

func webService() error {

if err := getRecords(); err != nil {
return fmt.Errorf("Error %s when calling DB", err)
}
return nil
}

func main() {
if err := webService(); err != nil {
if errors.Is(err, noRows) {
fmt.Printf("The searched record cannot be found. Error returned from DB is %s\n", err)
return
}
fmt.Println("unknown error when searching for records")
return
}
fmt.Println("webService call successful")
}

在上面的 ​​main​​ 函数中,我们利用 ​​Is​​ 函数检查这个错误是否包含 ​​noRows​​ 错误。

因为 ​​if errors.Is(err, noRows)​​ 并不满足,所以这个 ​​if​​ 块里并不会执行。为了使得这个错误生效,我们需要 ​​webService​​ 函数返回 ​​noRows​​ 错误时对其进行封装。

一种方法是在返回错误时使用 ​​%w​​ 格式指定符,而不是 ​​%s​​ 。因此,可以把返回错误的那一行代码修改为:

return fmt.Errorf("Error %w when calling DB", err)

这意味着新返回的错误包裹了原来的 ​​noRows​​,并且上述主函数 ​​if​​ 条件将成功生效,下面是修改的程序:

package main

import (
"errors"
"fmt"
)

var errNoRows = errors.New("no rows found")

func getRecords() error {
return errNoRows
}

func webService() error {

if err := getRecords(); err != nil {
return fmt.Errorf("error %w when calling DB", err)
}
return nil
}

func main() {
if err := webService(); err != nil {
if errors.Is(err, errNoRows) {
fmt.Printf("The searched record cannot be found. Error returned from DB is %s\n", err)
return
}
fmt.Println("unknown error when searching for records")
return
}
fmt.Println("webService call successful")
}

运行结果:

$ go run .
The searched record cannot be found. Error returned from DB is error no rows found when calling DB

As 函数

errors 包中的 ​​As​​ 函数尝试把输入的错误转换为目标错误类型。如果错误链中的任何一个错误与目标错误匹配,就返回 true。

package main

import (
"errors"
"fmt"
)

type DBError struct {
desc string
}

func (dbError DBError) Error() string {
return dbError.desc
}

func getRecords() error {
return DBError{
desc: "no rows found",
}
}

func webService() error {
if err := getRecords(); err != nil {
return fmt.Errorf("Error %w when calling DB", err)
}
return nil
}

func main() {
if err := webService(); err != nil {
var dbError DBError
if errors.As(err, &dbError) {
fmt.Printf("The searched record cannot be found. Error returned from DB is %s", dbError)
return
}
fmt.Println("unknown error when searching records")
return
}
fmt.Println("webservice call successful")
}

在上面的程序中,我们修改了 ​​getRecord​​ 函数,返回一个 ​​DBError​​ 类型的自定义错误。

在 ​​main​​ 函数中,我们试图将 ​​webService()​​ 函数调用返回的错误转换为 ​​DBError​​ 类型。​​if errors.As(err, &dbError)​​ 语句将会成功,因为我们已经把错误包起来了。运行这个代码,将会返回:

The searched record cannot be found. Error returned from DB is no rows found  

总结

程序可能会随时都会出现异常,需要我们在开发过程中提前做好异常处理。所以本文介绍了 Go 语言中的错误处理,Go 标准库提供的两种创建 ​​error​​ 的方式,并介绍了如何检查错误和如果传递错误。

希望本文能对你有所帮助,如果喜欢本文,可以点个关注.下一篇文章见!

宇宙古今无有穷期,一生不过须臾,当思奋争。

标签:errors,return,语言,err,错误,fmt,error,Go,错误处理
From: https://blog.51cto.com/yuzhou1su/5768321

相关文章

  • Go素数筛选分析
    Go素数筛选分析1.素数筛选介绍学习Go语言的过程中,遇到素数筛选的问题。这是一个经典的并发编程问题,是某大佬的代码,短短几行代码就实现了素数筛选。但是自己看完原理和代......
  • 浅谈C语言中的变量
    一.定义变量的方法就是类型+变量名+数值,比如:inta=12;shortage=22;charch='w';二.变量的分类1.全局变量2.局部变量全局变量:定义在代码块({})之外的变量。局部......
  • 浅谈C语言中的常量(字面常量、const修饰的常变量、#define定义的标识符常量、枚举常量
    一.常量不会变的量就是常量,比如性别,血型等;二.常量的分类1.字面常量2.const修饰的常变量3.#define定义的标识符常量4.枚举常量1.字面常量2.const修饰的常变量    在......
  • C语言学习笔记-地址和指针
    1背景嵌入式开发的时候频繁使用指针数组,以前本科的时候学的都忘了,因此接着学习GD32固件库学习的机会系统的看了一下书,并做出整理2查找地址:&运算符&主要是给出变量的地......
  • Go struct字段添加指针与不添加指针的区别
    packagemainimport("fmt")typeNstruct{Namestring`json:"name"`Ageint`json:"age"`B*BBB`json:"b"`}typeBBBstruct{yystringbbbyte......
  • C语言学习2--10/18
    常量: 不会变化的数据1.“hello”字符串常量,‘A’字符常量,-10整型常量,3.14浮点常量2.#definePI3.14,宏定义,推荐3.consta=10 const关键字,被该关键字......
  • C语言基础-数组得初始化
    #include<stdio.h>intmain(){inta[10];intsize=sizeof(a)/sizeof(a[0]);//计算数组得大小for(inti=0;i<size;i++){a[i]=i*100;......
  • 【C++】GoogleTest进阶之gMock
    gMock是什么当我们去写测试时,有些测试对象很单纯简单,例如一个函数完全不依赖于其他的对象,那么就只需要验证其输入输出是否符合预期即可。但是如果测试对象很复杂或者依赖......
  • 【自然语言处理(NLP)】基于SQuAD的机器阅读理解
    【自然语言处理(NLP)】基于SQuAD的机器阅读理解作者简介:在校大学生一枚,华为云享专家,阿里云专家博主,腾云先锋(TDP)成员,云曦智划项目总负责人,全国高等学校计算机教学与产业实践......
  • Golang编辑器 - GoLand安装
    GoLand官网https://www.jetbrains.com.cn/go/下载GoLand通过访问其官网进行安装包下载下载完成之后双击运行安装程序点击Next选择安装的路径下一步选择创建......