Go错误包装

在本教程中,我们将了解 Go 中的错误包装以及为什么我们需要错误包装。让我们开始吧。

什么是错误包装?

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

让我们编写一个小程序来理解这一点。

go 复制代码
 1package main
 2
 3import (
 4	"errors"
 5	"fmt"
 6)
 7
 8var noRows = errors.New("no rows found")
 9
10func getRecord() error {
11	return noRows
12}
13
14func webService() error {
15	if err := getRecord(); err != nil {
16		return fmt.Errorf("Error %s when calling DB", err)
17	}
18	return nil
19}
20
21func main() {
22	if err := webService(); err != nil {
23		fmt.Printf("Error: %s when calling webservice\n", err)
24		return
25	}
26	fmt.Println("webservice call successful")
27
28}

Run in playground

在上面的程序中,第 16 行我们发送getRecord函数调用时发生的错误的字符串描述。虽然这实际上看起来像是错误包装,

但事实并非如此。让我们在下一节中了解如何包装错误。

错误包装和 Is 函数

errors包中的Is函数报告链中的任何错误是否与目标匹配。

在本例中,在判断webService()函数是否出错,然后在去获取具体错误的类型,没获取到就返回unknown error when searching record

让我们修改这个程序的函数,并使用该函数检查链中是否有与错误匹配的错误。

fallback 复制代码
 1func main() {
 2	if err := webService(); err != nil {
 3		if errors.Is(err, noRows) {
 4			fmt.Printf("The searched record cannot be found. Error returned from DB is %s", err)
 5			return
 6		}
 7		fmt.Println("unknown error when searching record")
 8		return
 9
10	}
11	fmt.Println("webservice call successful")
12
13}

在上面的 main 函数中,第 3行该Is函数将检查保存的错误链中是否有任何错误err包含noRows错误。当前状态下的代码将不起作用,并且if中的条件将不起作用。上面的main函数都会失败。为了使其工作,我们需要noRows在函数返回错误时包装错误webService。一种方法是%w在返回错误时使用格式说明符而不是%s. 因此,如果我们修改返回错误的行

fallback 复制代码
1        return fmt.Errorf("Error %w when calling DB", err)

这意味着新返回的错误包含原始错误noRowsif中的条件。上面main函数就成功了。下面提供了带有错误包装的完整程序。

go 复制代码
package main

import (
	"errors"
	"fmt"
)

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

func getRecord() error {
	return noRows
}

func webService() error {
	if err := getRecord(); err != nil {
		return fmt.Errorf("Error %w 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", err)
			return
		}
		fmt.Println("unknown error when searching record")
		return

	}
	fmt.Println("webservice call successful")

}

Run in playground

当这个程序运行时,它会打印。

fallback 复制代码
The searched record cannot be found. Error returned from DB is Error no rows fou
nd when calling DB

作为函数

error 包中的 As将尝试将作为输入传递的错误转换为目标错误类型。如果错误链中的任何错误与目标匹配,则它将成功。如果成功,它将返回 true,并将目标设置为错误链中匹配的第一个错误。程序会让事情变得更容易理解:)

go 复制代码
package main

import (
	"errors"
	"fmt"
)

type DBError struct {
	desc string
}

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

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

func webService() error {
	if err := getRecord(); 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 record")
		return

	}
	fmt.Println("webservice call successful")

}

Run in playground

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

在 main 函数中,我们尝试将webService()函数调用返回的错误转换为DBError. 第32 行if的声明将会成功,因为我们已经在第 24 行包装了DBError从函数返回错误时的错误 webService() 。.运行该程序将打印

fallback 复制代码
The searched record cannot be found. Error returned from DB is no rows found

我们应该包装错误吗?

这个问题的答案是,视情况而定。如果我们包装错误,我们就会将其暴露给库/函数的调用者。我们通常不希望包装包含函数内部实现细节的错误。要记住的一件更重要的事情是,如果我们返回一个包装的错误,然后决定删除错误包装,则使用我们的库的代码将开始失败。因此,包装错误应被视为 API 的一部分,如果我们决定修改返回的错误,则应进行适当的版本更改。

我希望你喜欢这个教程。祝你有美好的一天 :)

相关推荐
ftpeak3 分钟前
《Cargo 参考手册》第二十一章:Cargo 包命令
开发语言·rust
陈一Tender9 分钟前
JavaWeb后端实战(登录认证 & 令牌技术 & 拦截器 & 过滤器)
java·开发语言·spring boot·mysql
Camel卡蒙9 分钟前
红黑树详细介绍(五大规则、保持平衡操作、Java实现)
java·开发语言·算法
jerryinwuhan14 分钟前
机器人模拟器(python)
开发语言·python·机器人
孤廖36 分钟前
吃透 C++ 栈和队列:stack/queue/priority_queue 用法 + 模拟 + STL 标准实现对比
java·开发语言·数据结构·c++·人工智能·深度学习·算法
驰羽44 分钟前
[GO]GORM中的Tag映射规则
开发语言·golang
非凡的世界1 小时前
深入理解 PHP 框架里的设计模式
开发语言·设计模式·php
小龙报1 小时前
《算法通关指南---C++编程篇(3)》
开发语言·c++·算法·visualstudio·学习方法·visual studio
凤山老林1 小时前
排序算法:详解插入排序
java·开发语言·后端·算法·排序算法
郝学胜-神的一滴2 小时前
Effective STL 第5条:区间成员函数优先于单元素成员函数
开发语言·c++·程序人生·stl·软件工程