Go 错误处理

错误

在Go中有一部分函数总是能成功的运行。比如strings.Contains和strconv.FormatBool函数;对于大部分函数而言,永远无法确保能否成功运行。

Go 语言通过内置的错误接口提供了非常简单的错误处理机制。

error类型是一个接口类型,这是它的定义:

type error interface { Error() string}

我们可以在编码中通过实现 error 接口类型来生成错误信息。

函数通常在最后的返回值中返回错误信息。使用errors.New 可返回一个错误信息:

func Sqrt(f float64) (float64, error) { if f < 0 { return 0, errors.New("math: square root of negative number") } // 实现}

这里有一个错误处理的例子

package mainimport “errors”import “fmt”// 按照惯例,错误通常是最后一个返回值并且是 error 类型,一个内建的接口。func f1(arg int) (int, error) {// errors.New 构造一个使用给定的错误信息的基本error 值。if arg == 42 {return -1, errors.New(“can’t work with 42”)}// 返回错误值为 nil 代表没有错误。return arg + 3, nil}// 通过实现 Error 方法来自定义 error 类型是可以的。// 这里使用自定义错误类型来表示上面的参数错误。type argError struct {arg intprob string}func (e *argError) Error() string {return fmt.Sprintf(“%d – %s”, e.arg, e.prob)}func f2(arg int) (int, error) {if arg == 42 {// 在这个例子中,我们使用 &argError 语法来建立一个新的结构体,并提供了 arg 和 prob 这个两个字段的值。return -1, &argError{arg, “can’t work with it”}}return arg + 3, nil}func main() {// 下面的两个循环测试了各个返回错误的函数。// 注意在 if行内的错误检查代码,在 Go 中是一个普遍的用法。for _, i := range []int{7, 42} {if r, e := f1(i); e != nil {fmt.Println(“f1 失败:”, e)} else {fmt.Println(“f1 工作:”, r)}}for _, i := range []int{7, 42} {if r, e := f2(i); e != nil {fmt.Println(“f2 失败:”, e)} else {fmt.Println(“f2 工作:”, r)}}// 你如果想在程序中使用一个自定义错误类型中的数据,你需要通过类型断言来得到这个错误类型的实例。_, e := f2(42)if ae, ok := e.(*argError); ok {fmt.Println(ae.arg)fmt.Println(ae.prob)}}

Deferred函数

defer语句经常被用于处理成对的操作,如打开、关闭、连接、断开连接、加锁、释放锁。通过defer机制,不论函数逻辑多复杂,都能保证在任何执行路径下,资源被释放。释放资源的defer应该直接跟在请求资源的语句后。

示例

package mainimport “fmt”import “os”func main() {// 假设我们想要创建一个文件,向它进行写操作,然后在结束时关闭它。// 这里展示了如何通过 defer 来做到这一切。f := createFile(“D:/defer.txt”) // f := createFile(“/tmp/defer.txt”)// 在 closeFile 后得到一个文件对象,我们使用 defer通过 closeFile 来关闭这个文件。这会在封闭函数(main)结束时执行,就是 writeFile 结束后。defer closeFile(f)writeFile(f)}func createFile(p string) *os.File {fmt.Println(“creating”)f, err := os.Create(p)if err != nil {panic(err)}return f}func writeFile(f *os.File) {fmt.Println(“writing”)fmt.Fprintln(f, “data”)}func closeFile(f *os.File) {fmt.Println(“closing”)f.Close()}

异常

Go的类型系统会在编译时捕获很多错误,但有些错误只能在运行时检查,如数组访问越界、空指针引用等。这些运行时错误会引起painc异常。

示例如下:

package mainimport (“fmt””os”)func main() {// 我们将在这个网站中使用 panic 来检查预期外的错误。这个是唯一一个为 panic 准备的例子。panic(“一个异常”)// panic 的一个基本用法就是在一个函数返回了错误值但是我们并不知道(或者不想)处理时终止运行。// 这里是一个在创建一个新文件时返回异常错误时的panic 用法。fmt.Println(“继续”)_, err := os.Create(“/tmp/file”)if err != nil {panic(err)}// 运行程序将会引起 panic,输出一个错误消息和 Go 运行时栈信息,并且返回一个非零的状态码。}

捕获异常

通常来说,不应该对panic异常做任何处理,但有时,也许我们可以从异常中恢复,至少我们可以在程序崩溃前,做一些操作。举个例子,当web服务器遇到不可预料的严重问题时,在崩溃前应该将所有的连接关闭;如果不做任何处理,会使得客户端一直处于等待状态。如果web服务器还在开发阶段,服务器甚至可以将异常信息反馈到客户端,帮助调试。

如果在deferred函数中调用了内置函数recover,并且定义该defer语句的函数发生了panic异常,recover会使程序从panic中恢复,并返回panic value。导致panic异常的函数不会继续运行,但能正常返回。在未发生panic时调用recover,recover会返回nil。

示例:

package mainimport “fmt”func main() {// 这里我们对异常进行了捕获defer func() {if p := recover(); p != nil {err := fmt.Errorf(“internal error: %v”, p)if err != nil {fmt.Println(err)}}}()// 我们将在这个网站中使用 panic 来检查预期外的错误。这个是唯一一个为 panic 准备的例子。panic(“一个异常”)}

分享不易,欢迎大家点赞关注~~~ 感谢大家阅读!

郑重声明:本文内容及图片均整理自互联网,不代表本站立场,版权归原作者所有,如有侵权请联系管理员(admin#wlmqw.com)删除。
上一篇 2022年6月20日 09:16
下一篇 2022年6月20日 09:16

相关推荐

联系我们

联系邮箱:admin#wlmqw.com
工作时间:周一至周五,10:30-18:30,节假日休息