前言
本文是探讨的是"go语言中如何优雅地处理错误"
观察go语言源码
在go语言的内置函数中,很多的函数都会返回一个error,特别是在与文件读写操作的相关的函数的时候,基本上都会有返回error,返回这个的好处是用来辨别是否出错,把结果直接告诉你,以便你进行下一步操作,而不是因为这个报错,就导致直接就退出程序
比如说最简单的Printf函数
其实在后面也是有返回一个error的,但是由于这个函数过于简单,所以我们就不常用而已,就我个人而言,我建议我们自己写代码的时候,如果有比较复杂的操作,也可以进行error的处理,下面是一个简单的示例
go
func division(a, b int) (c int, err error) {
if b == 0 {
return 0, errors.New("被除数为0")
}
c = a / b
return c, nil
}
os.Exit和panic的差别
最显著的区别就是,panic引发程序终止的话,我们可以在defer关键字标记的延时执行的函数里面,进行后续错误处理,可以让程序继续运行,而os.Exit就会直接终止,不利于我们进行下一步操作
下面是详细的解释:
os.Exit(1)用于无法恢复的错误,它会立即终止程序的运行,并返回给操作系统一个退出状态码。退出状态码1通常用于表示程序发生了错误。os.Exit函数不会打印任何错误信息,因此无法得知具体的错误原因。
go
package main
import (
"fmt"
"os"
)
func main() {
// 终止程序的运行,并返回退出状态码1
fmt.Println("Before os.Exit")
os.Exit(1)
fmt.Println("After os.Exit") // 不会执行到这里
}
panic用于表示程序发生了无法处理的错误,它会导致程序崩溃并打印错误信息。panic函数会触发一系列的延迟函数(defer)的执行,然后终止程序的运行。可以使用recover函数来捕获panic并进行处理。
go
package main
import (
"fmt"
)
func main() {
// 触发panic,程序崩溃
fmt.Println("Before panic")
panic("Something went wrong")
fmt.Println("After panic") // 不会执行到这里
}
总结:
os.Exit(1)用于无法恢复的错误,立即终止程序并返回退出状态码。
panic用于无法处理的错误,导致程序崩溃并打印错误信息。
创造性的错误处理
2015年1月,GO博客发表了一篇关于错误处理的文章"Error are values",该文描述了一种简单处理方法,具体可以看下面的代码
go
type safeWriter struct {
w io.Writer
err error // 存储错误的地方
}
func (sw *safeWriter) writer (s string){
if sw.err != nil { // 如果前面有错误发生,就直接跳过写入操作
return
}
_,sw.err= fmt.Fprintln(sw.w,s) // 尝试写入文本行并储存可能出现的错误
}
这种错误处理的方法使用了一个自定义的类型safeWriter,其中包含了一个io.Writer接口和一个错误err。在写入操作之前,先检查err是否为空,如果不为空则直接跳过写入操作,避免出现更多错误。在写入操作之后,通过fmt.Fprintln函数尝试写入文本行,并将可能出现的错误存储在err中。
这种错误处理的方法有以下优点:
可以避免错误的连锁反应:如果前面发生了错误,后续的写入操作会被跳过,避免了错误的扩散和更多的错误发生。
错误信息可以被保存:通过将错误信息存储在err中,可以在需要时获取并进行处理,比如将错误信息记录到日志中或者返回给调用者。
然而,这种错误处理的方法也有一些潜在的问题:
可能会忽略某些错误:如果在写入操作之后发生了错误,但是没有将其存储在err中,那么这个错误就会被忽略,可能导致问题的难以追踪和修复。
可能会导致部分写入数据丢失:如果在写入操作之后发生了错误,但是没有进行处理,那么部分数据可能无法成功写入,导致数据丢失。
总的来说,这种错误处理的方法在简单场景下是有效的,可以避免错误的连锁反应,并且可以将错误信息保存下来。但是在复杂的场景下,可能需要更加细致的错误处理机制来确保所有的错误都被处理和记录。