Go Error 错误处理总结

2023-05-16

Go Error

一. 设计理念

  • 简单
  • 考虑成功而不是只有成功
  • 没有隐藏的控制流
  • 完全交给调用者控制 error
  • Error are values(Rob Pike)

二. 错误与异常

go 中的错误处理主要使用到errorpanic,其中 error 使用居多

error

error 主要有以下几个特点

  • 不会造成程序运行结束
  • error 仅仅是一个返回值
  • 将问题抛给调用者处理,可以不处理,但建议处理
  • 明确告诉调用者需要处理错误,一定程度上确保调用者会处理(强烈建议)

panic

panic 主要有以下几个特点

  • 调用者未处理时可能会造成整个程序运行结束
  • 隐式的,调用者不知道被调用方法是否会触发 panic,所以无法确保调用者一定会处理
  • 通常用作较大错误,程序无法执行(fatal error)时抛出(慎用)

三. 几种错误实践方案

1. Sentinel Error(预定义错误)

(1. 标准库中使用到预定义错误的例子

尽可能提前定义好所有需要的错误类型及错误代码,方便业务中使用及判断

// go/1.17.5/libexec/src/bufio/bufio.go

var (
	ErrInvalidUnreadByte = errors.New("bufio: invalid use of UnreadByte")
	ErrInvalidUnreadRune = errors.New("bufio: invalid use of UnreadRune")
	ErrBufferFull        = errors.New("bufio: buffer full")
	ErrNegativeCount     = errors.New("bufio: negative count")
)

(2. 使用 error 对象进行 == 判断,而非 error 的内容

error 的内容是为了方便调试或者日志记录,而非方便程序控制

仅使用错误内容判断错误,而进行错误逻辑处理可能造成隐患,因为可能存在相同错误内容,但不同错误对象的情况。errors.New() 时也会返回当前对象的指针,以确保每个新建一个对象都是唯一的。

package errors

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wbfuhipu-1655983351596)(/Users/zsl/Library/Application Support/typora-user-images/image-20220623190257837.png)]

(3. 预定义 error,需要放置在公共包中,且需要有文档描述

Sentinel Error 通常被用作底层工具开发使用,第三方包如果大量使用可能会导致循环导入问题(import loop)

需要放置在公共包中,且需要有文档描述。(注意:阅读源码发现方法的返回值仍然是 error

// ErrShortWrite means that a write accepted fewer bytes than requested (写入的字节数少于请求的)
// but failed to return an explicit error. 
var ErrShortWrite = errors.New("short write")

// errInvalidWrite means that a write returned an impossible count. (写入的返回是无法计数的)
var errInvalidWrite = errors.New("invalid write result")

// ErrShortBuffer means that a read required a longer buffer than was provided.(读取的字节数大于提供的)
var ErrShortBuffer = errors.New("short buffer")
......

2. Error Types(错误类型)

(1. 使用自己封装的 type 作为 error 使用

// PathError records an error and the operation and file path that caused it.
type PathError struct {
	Op   string
	Path string
	Err  error
}

func (e *PathError) Error() string { return e.Op + " " + e.Path + ": " + e.Err.Error() }

(2. 判断错误时可使用断言的方式做类型转换

// underlyingError returns the underlying error for known os error types.
func underlyingError(err error) error {
	switch err := err.(type) {
	case *PathError:
		return err.Err
	case *LinkError:
		return err.Err
	case *SyscallError:
		return err.Err
	}
	return err
}

3. Opaque error(模糊错误)

(1. 仅仅判断 error 是否为 nil,而不关心具体是什么 error

(2. assert errors for behaviour, no type(可暴露行为,而不暴露类型)

4. Warp error(包装错误)

将错误向上抛,在某个地方统一处理

  • 仅处理错误一次
  • 需要使用预定义错误、类型断言、行为判定时不能使用 fmt.Errorf() 做转换
  • 通过日志记录错误是需要记录详细信息(即仅凭日志就可以粗略看出具体问题,位置信息、参数信息、错误信息等)

使用github.com/pkg/errors 做错误包装,

  • 程序中出现错误使用 errors.New() 或者 errors.Errorf() 返回错误
  • 调用其他包,返回错误时可直接返回
  • 调用其他库报错时,使用error.Warp()或者error.Warpf()保存堆栈信息(适用于标准库)
  • 程序顶部使用%+v方式打印错误堆栈信息
  • errors.Cause 打印原始错误,可以使用预定义错误、类型断言、行为判定等配合使用
  • 业务库中可以使用,封装库避免使用 Wrap error ,否则可能出现两层堆栈信息

四. 错误处理写法优化策略

1. 错误流缩进

indented flow is for errors

无错误的代码正常执行,而不是缩进的代码(推荐判断 err != nil不推荐err == nil

func test() {
	a, err := funca()
	if err != nil { //推荐
		//do error handling
	}
	//do something

	if err == nil { //不推荐
		//do something
	}
	// do error handling
}

2. 避免无效判断

eliminate error handling by eliminate errors

避免无效的错误判断

func test() error { //不推荐
	err := funca()
	if err != nil {
		return err;
	}
	return nil;
}

func test() error { //推荐
	return funca()
}

3. 收集错误

可以通过错误记录或者收集的方式减少错误判断

//go/1.17.5/libexec/src/bufio/scan.go
// advance consumes n bytes of the buffer. It reports whether the advance was legal.
func (s *Scanner) advance(n int) bool {
	if n < 0 {
		s.setErr(ErrNegativeAdvance)
		return false
	}
	if n > s.end-s.start {
		s.setErr(ErrAdvanceTooFar)
		return false
	}
	s.start += n
	return true
}

// setErr records the first error encountered.
func (s *Scanner) setErr(err error) {
	if s.err == nil || s.err == io.EOF {
		s.err = err
	}
}

未使用错误收集方式

type Header struct {
	Key, Value string
}
type Status struct {
	Code   int
	Reason string
}
func WriteResponse(w io.Writer, st Status, headers []Header, body io.Reader) error {
	_, err := fmt.Fprintf(w, "HTTP/1.1 %d %s\r\n", st.Code, st.Reason)
	if err != nil {
		return err
	}
	for _, h := range headers {
		_, err := fmt.Fprintf(w, "%s: %s\r\n", h.Key, h.Value)
		if err != nil {
			return err
		}
	}
	if _, err := fmt.Fprint(w, "\r\n"); err != nil {
		return err
	}
	_, err = io.Copy(w, body)
	return err
}

使用错误收集方式

type errWriter struct {
	io.Writer
	err error
}

func (e *errWriter) Write(buf []byte) (int, error) {
	if e.err != nil {
		return 0, e.err
	}
	var n int
	n, e.err = e.Writer.Write(buf)
	return n, nil
}

func WriteResponse(w io.Writer, st Status, headers []Header, body io.Reader) error {
	ew := &errWriter{Writer: w}
	fmt.Fprintf(ew, "HTTP/1.1 %d %s\r\n", st.Code, st.Reason)
	for _, h := range headers {
		fmt.Fprintf(ew, "%s: %s\r\n", h.Key, h.Value)
	}
	fmt.Fprint(ew, "\r\n")
	io.Copy(ew, body)
	return ew.err
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Go Error 错误处理总结 的相关文章

随机推荐

  • 学会 Go 中的时间处理

    作为程序员 xff0c 我们经常需要对时间进行处理 在 Go 中 xff0c 标准库 time 提供了对应的能力 本文将介绍 time 库中一些重要的函数和方法 xff0c 希望能帮助到那些一遇到 Go 时间处理问题就需要百度的童鞋 应对时
  • Ubuntu修改文件权限

    Linux内的一切皆文件 xff0c 所以对于Linux下文件的管理就十分的重要了 Linux下的文件权限分为三种 xff1a r xff08 读 xff09 xff0c w xff08 写 xff09 xff0c x xff08 执行 x
  • Libnet简单学习

    简单了解后 xff0c 建议直接查看源码 xff0c 以获得其他函数 xff1a libnet libnet functions h File Reference 本文仅列举个别常用函数 libnet工作流程 xff08 1 xff09 通
  • Java模拟生产者消费者模型【仅代码 + 注释】

    模拟仓库容量为1 xff0c 1个消费者 xff0c 1个生产者的生产者消费者模型 span class token keyword package span span class token namespace com span clas
  • PHP8.2 Apache24 Windows10安装步骤

    PHP8 2 Apache24 Windows10安装步骤 1 官网地址 https httpd apache org download cgi 修改1 xff1a Define SRVROOT D WorkSoft Apache Apac
  • navicat乱码和激活

    乱码 xff1a 1 将export LANG 61 34 zh CN UTF 8 34 2 工具 gt 选项下常规 编辑器 xff0c 记录下的字体选Noto sans CJK相近字体 激活 xff1a 第一次执行start navica
  • OnclickListener

    相信很多像我一样的新手学习ANDROID开发会遇到这个问题 xff0c 通过这几天的归类和总结 xff0c 将我的理解写在下面 xff0c 欢迎大家一起前来讨论 xff1a 以按钮BUTTON的监听事件为例 xff0c 以下的监听实现都是等
  • 关于Activity的onNewIntent方法

    前言 onNewIntent方法想必大家都知道 xff0c 是和Activity的启动模式结合起来使用的 xff0c 可以这个方法具体什么情况下被调用 xff0c 如何使用你清楚了吗 xff1f 今天就来一探究竟 xff0c 扫清疑惑 实验
  • 【算法学习】二分查找 binary-search (Java 参考)

    题目描述 给定一个 n 个元素有序的 xff08 升序 xff09 整型数组 nums 和一个目标值 target xff0c 写一个函数搜索 nums 中的 target xff0c 如果目标值存在返回下标 xff0c 否则返回 1 思路
  • 【算法学习】二维数组检索 search-a-2d-matrix(Java)

    题目描述 请写出一个高效的在m n矩阵中判断目标值是否存在的算法 xff0c 矩阵具有如下特征 xff1a 每一行的数字都从左到右排序 每一行的第一个数字都比上一行最后一个数字大 例如 xff1a 对于下面的矩阵 xff1a 1 3 5 7
  • 【算法学习】二维数组查找(Java)

    一 题目描述 此题源于 剑指 offer 在一个二维数组中 xff08 每个一维数组的长度相同 xff09 xff0c 每一行都按照从左到右递增的顺序排序 xff0c 每一列都按照从上到下递增的顺序排序 请完成一个函数 xff0c 输入这样
  • 【算法学习】链表数相加(Java)

    一 题目表述 给定两个代表非负数的链表 xff0c 数字在链表中是反向存储的 xff08 链表头结点处的数字是个位数 xff0c 第二个结点上的数字是十位数 xff09 xff0c 求这个两个数的和 xff0c 结果也用链表表示 输入 xf
  • 【算法学习】最长公共子序列(Java)

    一 题目描述 给定两个字符串 text1 和 text2 xff0c 返回这两个字符串的最长公共子序列的长度 一个字符串的 子序列 是指这样一个新的字符串 xff1a 它是由原字符串在不改变字符的相对顺序的情况下删除某些字符 xff08 也
  • JFugue4.0 中文说明

    简介 由音符 八度 音长 音色 xff08 乐器 xff0c 默认乐器为钢琴 xff09 组成 和弦 连音 速冻 控制器 键签名 Jfugue 可以简单并且允许工程师去快速创建音乐的原因是 MusicString xff0c 一个特殊格式描
  • 【算法学习】有效括号 valid-parentheses (Java 参考)

    题目描述 给定一个只包括 xff0c xff0c xff0c xff0c xff0c 的字符串 xff0c 判断字符串是否有效 有效字符串需满足 xff1a 左括号必须用相同类型的右括号闭合 左括号必须以正确的顺序闭合 注意空字符串可被认为
  • 【算法学习】约瑟夫环(含公式思想总结)(Java)

    目录 一 题目描述二 思路分析思路1 xff1a 模拟思路2 xff1a 数学方法递推公式 xff1a 推导思路why 为什么可以倒推how 如何倒推 三 参考代码四 测试连接 一 题目描述 这个问题是以弗拉维奥 约瑟夫命名的 xff0c
  • Ubuntu下matplotlib中文乱码

    原因 xff1a 由于matplotlib的默认安装字体不支持中文格式 解决思路 xff1a 将默认字体换成可以支持中文字体包 matplotlib默认的字体文件为 anaconda3 lib python3 7 site packages
  • 【算法学习】n个骰子的点数(Java)

    目录 一 题目描述二 思路分析核心公式公式推导依据 三 参考代码四 测试连接 一 题目描述 把n个骰子扔在地上 xff0c 所有骰子朝上一面的点数之和为s 输入n xff0c 打印出s的所有可能的值出现的概率 你需要用一个浮点数数组返回答案
  • 自动复制粘贴“机器人”(go)

    背景 最近遇到个问题 xff0c 需要将 html 批量转换为 markdown xff0c 尝试过很多转换库结果并不理想 xff0c 发现通过复制粘贴的方式效果十分不错 xff08 mac xff0c 从chrome浏览器 xff0c 复
  • Go Error 错误处理总结

    Go Error 一 设计理念 简单考虑成功而不是只有成功没有隐藏的控制流完全交给调用者控制 errorError are values xff08 Rob Pike xff09 二 错误与异常 go 中的错误处理主要使用到error 和