golang当中的错误处理--筑基五层

简介: 本文主要介绍了Golang中的错误处理机制。Golang不像Java有try/catch异常处理,而是采用defer-panic-and-recover机制。文章首先讲解了Golang如何通过返回错误对象来处理普通错误,并以实例演示定义和使用错误。接着探讨了运行时异常与panic的使用场景,强调panic应仅用于无法恢复的严重错误。随后介绍了如何通过recover从panic中恢复程序执行。最后,文章展示了如何利用闭包优雅地统一处理错误,减少代码冗余,提高可读性。这些内容为开发者提供了全面的Golang错误处理指南。

写在前面

在前面的几篇文章当中,我们主要是学习了Golang当中文件的读写以及数据的编码方式相关的知识。接下来,我们将开始来学习Golang中的错误处理

Golang的错误处理模式

Go并没有像Java那样的一套try/catch异常处理机制,它不能执行抛异常操作。它使用的是一套defer-panic-and-recover机制。

那么,Golang是怎么处理错误的呢?它的处理方式是这样的:?通过在函数和方法中返回错误对象作为它们的唯一或最后一个返回值——如果返回 nil,则没有错误发生——并且主调(calling)函数总是应该检查收到的错误。而上面提到的panic and recover 是用来处理真正的异常(无法预测的错误)而不是普通的错误。

定义一个错误

在Go中有一个预先定义好的error类型的接口

go

体验AI代码助手

代码解读

复制代码

type error interface {
    Error() string
}

errors这个包当中有一个errorString的结构体实现了这个接口

go

体验AI代码助手

代码解读

复制代码

type errorString struct {
	s string
}

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

错误值s用来表示异常状态,当你需要一个新的错误类型,都可以用 errors包的 errors.New 函数接收合适的错误信息来创建。

请看下面这个例子:

go

体验AI代码助手

代码解读

复制代码

package main

import (
	"errors"
	"fmt"
)

func main() {

	hello, err := sayHello("")
	if err!=nil {
		log.Default().Println(err)
		return
	}
	fmt.Println(hello)
}

func sayHello(name string) (string,error) {

	if ""==name {
		return "", errors.New("name 不能是一个空字符串")
	}

	return "Hello "+name,nil
}

输出:

yaml

体验AI代码助手

代码解读

复制代码

2021/10/02 14:53:16 name 不能是一个空字符串

在这个例子当中,如果函数sayHello收到的参数name是一个空串的话,函数将返回一个错误,主调函数当中必须对错误进行处理。如果有不同错误条件可能发生,那么可以对实际的错误使用类型断言或类型判断(type-switch),然后根据错误场景做一些补救和恢复操作。

在Golang当中,对于错误类型以及错误变量有以下的命名规范:错误类型以 “Error” 结尾,错误变量以 “err” 或 “Err” 开头。

运行时异常与panic

在Golang中,当发生了类似于数组下标越界或者是类型断言失败这样的运行时错误时,Go将会触发运行时panic (程序崩溃)。

这里需要注意的一点是,panic的使用条件应当是相当严苛的并且是不能由程序自行恢复的。发生这样的错误时,意味着程序将不能为我们提供服务,而需要终止程序。

请看下面的例子:

go

体验AI代码助手

代码解读

复制代码

package main

import "fmt"

func main() {

	fmt.Println("发生程序崩溃前")
	panic("程序崩溃了")
    // 下面这条语句将不会被执行
	fmt.Println("已产生panic...")

}

运行这个程序将导致它抛出panic,它打印出错误信息和goroutine痕迹,并以非零状态(非正常状态)退出。

输出:

go

体验AI代码助手

代码解读

复制代码

发生程序崩溃前
panic: 程序崩溃了

goroutine 1 [running]:
main.main()
	G:/06_golangProject/golang/src/go_code/err/panic_demo/main.go:8 +0xa5

panic使用场景与panicking

在前面我们介绍了panic。在这里,我们来探讨一个问题:我们应该给在什么时候使用panic呢?

我个人理解,panic适用于这样的场景:当发生的错误是我们不知道要怎么处理时(不打算优雅处理时),这种错误必须要中止程序运行了,那么,我们将使用到panic。

例如在我们学习Golang读写数据时,编写的小程序mycat(它用于打印文件内容,博文点击此处传送)当中,程序根据用户输入的参数打开一个指定的文件,当文件不存在时,就可以抛出panic.

在多层嵌套的函数调用中调用 panic,可以马上中止当前函数的执行,所有的 defer 语句都会保证执行并把控制权交还给接收到 panic 的函数调用者。这样向上冒泡直到最顶层,并执行(每层的) defer,在栈顶处程序崩溃,并在命令行中用传给 panic 的值报告错误情况:这个终止过程就是 panicking

注意: 不要随意地用 panic 中止程序,应当尽力补救错误让程序能继续执行。

从 panic 中恢复(Recover)

学会了panic的基本使用,我们需要思考一个问题,那就是,对于panic,我们只能中止程序运行吗?有没有从panic中恢复的方法?

这时,我们将使用到内建函数recover() 。它用于从panic或错误场景当中恢复,从而使得程序可以从panicking重新获得控制权,停止终止过程进而恢复程序的正常运行。

请看下面的例子:

go

体验AI代码助手

代码解读

复制代码

package main

import "fmt"

func main() {

	defer func() {
		fmt.Println("done...")
		if err:=recover();err!=nil{
			fmt.Println(err)
			func() {
				fmt.Println("end...")
			}()
		}
	}()

	fmt.Println("start...")
	panic("this is a error")
}

输出:

erlang

体验AI代码助手

代码解读

复制代码

start...
done...
this is a error
end...

注意:

  1. recover 只能在 defer 修饰的函数中使用:用于取得 panic 调用中传递过来的错误值,如果是正常执行,调用 recover 会返回 nil,且没有其它效果。
  2. panic 会导致栈被展开直到 defer 修饰的 recover() 被调用或者程序中止。

以上就是defer-panic-and-recover机制。你也可以将其理解为像if、for一样的流程控制。它类似于Java当中的try...catch 机制。

我们在使用panic时,可以遵循Go 库的原则:即使在包的内部使用了 panic,在它的对外接口(API)中也必须用 recover 处理成返回显式的错误。

自定义包中的错误处理和 panicking

在自定义包中的错误处理时,我们遵循以下原则:

  1. 在包内部,总是应该和panicrecover: 不应该显式的超出包范围的panic ()
  2. 向包的调用者返回错误值

请看下面的例子:

go

体验AI代码助手

代码解读

复制代码

package main

import (
	"fmt"
)

func main() {
	var name []string
	name = append(name, "韩立","","南宫婉")

	for i, _ := range name {

		err := SayHello(name[i])
		if err != nil {
			fmt.Println(err)
		}
		continue
	}

}

func SayHello(name string) (err error) {
	defer func()  {
		if r := recover(); r != nil {
			//var ok bool
				err = fmt.Errorf("%v",r)
		}
	}()
	// 注意: 抛出panic的函数必须在defer之后调用
	doSayHello(name)
	return nil

}

func doSayHello(name string) {
	if len(name)==0 {
		panic("名字不能是一个空字符串")

	}
	fmt.Printf("hello %s\n", name)


}

go

体验AI代码助手

代码解读

复制代码

hello 韩立
名字不能是一个空字符串
hello 南宫婉

在这个例子当中,包内从panicrecover,并返回给调用者错误提示,使得程序可以继续往下执行。重要的事情多说几遍,panic的使用应当严格地限制其场景,尽可能地使程序从panicrecover

使用闭包优雅地处理错误

像上面的代码一样,每当调用函数时,必须检查错误是否发生,这将增加代码的重复率,到处充斥着错误检查,这一点都不优雅。那么,在Golang中有没有机制像Java当中一样,可以统一地对错误进行处理呢?

请看下面的例子:

go

体验AI代码助手

代码解读

复制代码

package main

import (
	"errors"
	"fmt"
)

var f1 = func(name string) error{
	if name != "" {
		println("Hello! ",name)

	}else {
		return errors.New("姓名不能为空")
	}

	return nil
}

func main() {

	errorHandler("",f1)

}

func errorHandler(name string,f1 func(s string) error)  {
	defer func() {
		if r := recover();r!=nil {
			fmt.Println(r)
		}
	}()

	err := f1(name)
	check(err)

}

func check(e error) {
	if e != nil {
		panic(e)
	}
}

程序输出:

体验AI代码助手

代码解读

复制代码

姓名不能为空

在这个例子当中,errorHandler是一个包装函数。它有两个参数,一个是string 类型的参数name,另一个则是匿名函数f1 。在errorHandler函数当中将调用函数f1,并通过check函数对程序执行进行校验这时,所有的错误都将被recover.


转载来源:https://juejin.cn/post/7040346824531509255

相关文章
|
9月前
|
Go
Golang语言错误处理机制
这篇文章是关于Golang语言错误处理机制的教程,介绍了使用defer结合recover捕获错误、基于errors.New自定义错误以及使用panic抛出自定义错误的方法。
103 3
|
11月前
|
中间件 测试技术 Go
Golang中的错误处理最佳实践
【7月更文挑战第10天】在Golang中,错误处理是核心且重要的。最佳实践包括:定义明确的错误类型,使用错误链(如`%w`包装错误),始终检查错误(避免忽略),谨慎使用`panic`和`recover`,利用多值返回处理错误,标准化错误处理逻辑,并确保测试错误处理代码。这些做法有助于构建健壮和可维护的程序。
|
存储 Java Go
100天精通Golang(基础入门篇)——第23天:错误处理的艺术: Go语言实战指南
100天精通Golang(基础入门篇)——第23天:错误处理的艺术: Go语言实战指南
103 0
|
网络协议 Go
Golang中错误处理
Golang中错误处理
146 0
|
存储 搜索推荐 网络协议
人非圣贤孰能无过,Go lang1.18入门精炼教程,由白丁入鸿儒,Go lang错误处理机制EP11
人非圣贤,孰能无过,有则改之,无则加勉。在编程语言层面,错误处理方式大体上有两大流派,分别是以Python为代表的异常捕获机制(try....catch);以及以Go lang为代表的错误返回机制(return error),前者是自动化流程,模式化的语法隔离正常逻辑和错误逻辑,而后者,需要将错误处理判断编排在正常逻辑中。虽然模式化语法更容易让人理解,但从系统资源开销角度看,错误返回机制明显更具优势。
人非圣贤孰能无过,Go lang1.18入门精炼教程,由白丁入鸿儒,Go lang错误处理机制EP11
Golang:error包错误处理
Golang:error包错误处理
127 0
|
9月前
|
Go
Golang语言之管道channel快速入门篇
这篇文章是关于Go语言中管道(channel)的快速入门教程,涵盖了管道的基本使用、有缓冲和无缓冲管道的区别、管道的关闭、遍历、协程和管道的协同工作、单向通道的使用以及select多路复用的详细案例和解释。
286 4
Golang语言之管道channel快速入门篇
|
9月前
|
Go
Golang语言文件操作快速入门篇
这篇文章是关于Go语言文件操作快速入门的教程,涵盖了文件的读取、写入、复制操作以及使用标准库中的ioutil、bufio、os等包进行文件操作的详细案例。
130 4
Golang语言文件操作快速入门篇
|
9月前
|
Go
Golang语言之gRPC程序设计示例
这篇文章是关于Golang语言使用gRPC进行程序设计的详细教程,涵盖了RPC协议的介绍、gRPC环境的搭建、Protocol Buffers的使用、gRPC服务的编写和通信示例。
248 3
Golang语言之gRPC程序设计示例

推荐镜像

更多