错误处理
1. error接口
Go语言引入了一个关于错误处理的标准模式。即error接口,该接口定义如下:
type error interface {
Error() string
}
对于大多数函数,如果要返回错误,都可以按照如下方式返回,将error作为多重返回值的最后一个,
但这并不是强制的要求。
func Foo(param int) (n int, err error) {
}
n, err := Foo(0)
if err != nil {
//错误处理
} else {
//使用返回值n
}
1.1 error接口使用
应为Go语言灵活的特性,我们不用指定一个类型从error接口继承,我们只需要实现error接口所有的
函数即可。
下面代码是Go库中的实际函数
type PathError struct {
Op string
Path string
Err string
}
func (e *PathError) Error() string {
return e.Op + " " + e.Path + ": " + e.Err.Error()
}
实现这个错误类之后,我们就可以直接返回PathError变量了。当syscall.stat()失败返回err时,将
该err包包装到一个PathError对象中返回
func Stat(name string) (fi FileInfo, err error) {
var stat syscall.Stat_t
err = syscall.Stat(name, &stat)
if err != nil {
return nil, &PathError("stat", name, err)
}
return fileInfoFromStat(&stat, name), nil
}
如果我们处理错误时,希望获取详细的信息。那就要用到Go语言中的类型转换了。
Go语言的类型转换与其他语言有所不同。使用方法如下:
fi, err := os.Stat("a.txt")
if err != nil {
if e, ok := err.(*os.PathError); ok && e.Err != nil {
//获取PathError中的变量,并处理
}
}
上面的例子中e, ok := err.(*os.PathError)
就是将err的类型转换为PathError类型。
2. defer
关键字defer是Go语言中的一个特性。
在C语言中,文件句柄打开后,在不使用时要记得关闭,
所以我们要考虑非常多的情况,比如异常时的关闭和清理工作。defer关键字就很好的解决了这个
问题。比如下面的例子:
func CopyFile(dist, src string) (w int64, err error) {
srcFile, err := os.Open(scr)
if err != nil {
return
}
defer srcFile.Close()
dstFile, err := os.Create(dst)
if err != nil {
return
}
defer dstFile.Close()
return io.Copy(dstFile, srcFile)
}
即使CopyFile函数中抛出异常,Go仍然会保证defer修饰的函数能正常执行。如果你觉得
一句话完不成清理工作,也可以在defer后面加一个匿名函数。
defer func() {
//复杂的清理工作
}
defer遵循先进后出原则,一个函数中,最后一个defer会先执行。当然,如果你为defer的执行
顺序烦恼时,你有必要重新审视一下你的架构了。
3. panic和recover
Go语言引入了panic()和recover()函数来报告和处理运行时的错误和程序中的错误。
3.1 错误处理流程
在一个函数执行过程中调用panic()函数时,正常的函数执行流程将立刻终止,但是函数中
之前用defer关键字延迟执行的语句会正常执行。之后该函数将返回调用函数,并导致逐层
向上执行panic流程,直至所属的goroutine中所有正在执行的函数被终止。错误信息将被报告,
包括调用panic()函数时传入的参数。
3.2 recover
recover用于终止错误处理流程。一般情况下,recover()应该在一个使用defer关键字的函数中执行。以保证有效的截取错误处理流程。如果在没有发生异常的goroutine中明确调用恢复过程,会导致该goroutine所属进程打印异常信息后退出。
使用场景:
defer func() {
if r := recover(); r != nil {
log.Printf("Runtime error caught: %v", r)
}
} ()
foo()
无论foo()函数中是否触发了错误处理流程。该匿名defer函数都会在函数退出时执行。如果
foo中触发了错误处理流程。recover将得以终止流程,如果传给panic函数的参数不为nil,
还会打印错误信息。