error接口
Golang中有一个关于错误处理的标准模式,即:error
接口。
type error interface {
Error() string
}
对于大多数函数,如果要返回错误,大致上都可以定义为如下模式:
func Foo(param int)(n int, err error) {
// ...
}
将error作为多种返回值中的一个,但是这并非强制要求。
调用代码时建议按如下方式处理错误情况:
n, err := Foo(0)
if err != nil {
// 错误处理
} else {
// 使用返回值n
}
defer关键字
关键字defer
时Golang中一个非常有意思的特性,用于解决资源的释放问题。
示例代码如下:
func CopyFile(dst, src string)(w int64, err error) {
srcFile, err := os.Open(src)
if err != nil {
return
}
// 使用关键字defer标注确保这里的资源需要释放
defer srcFile.Close()
dstFile, err := os.Create(dst)
if err != nil {
return
}
// 使用关键字defer标注确保这里的资源需要释放
defer dstFile.Close()
return io.Copy(dstFile, srcFile)
}
使用defer
关键字可以确保:即使其中的Copy()
函数抛出异常,仍然会保证dstFile
和srcFile
会被正常关闭。
如果觉得一句话干不完清理的工作,也可以使用在defer后加一个匿名函数的做法:
defer func() {
// 多条语句执行清理工作
} ()
一个函数中可以存在多个defer语句,defer语句的调用是遵照先进后出
的原则,即最后一个defer语句将最先被执行。
panic()和recover()
Golang中的两个内置函数panic()
和recover()
用于报告和处理运行时错误和程序中的错误场景:
func panic(interface{})
func recover() interface{}
当在一个函数执行过程中调用panic()
函数时,正常的函数执行流程将立即终止,但函数中之前使用defer
关键字延迟执行的语句将正常展开执行,之后该函数将返回到调用函数,并导致逐层向上执行panic流程,直至所属的goroutine中所有正在执行的函数被终止。错误信息将被报告,包括在调用panic()
函数时传入的参数,这个过程称为错误处理流程。
从panic()
的参数类型interface{}我们可以得知,该函数接收任意类型的数据,比如整型、字符串、对象等。
示例代码如下:
panic(404)
panic("network broken")
panic(errors.New("file not exists"))
recover()
函数用于终止错误处理流程。
一般情况下,recover()
应该在一个使用defer
关键字的函数中执行以有效截取错误处理流程。
如果没有在发生异常的goroutine中明确调用恢复过程(使用recover关键字),会导致该goroutine所属的进程打印异常信息后直接退出。
如下描述一个使用recover()
函数的场景:
对于foo()函数的执行要么心里没底感觉可能会触发错误处理,或者自己在其中明确加入了按特定条件触发错误处理的语句,那么可以用如下方式在调用代码中截取recover()。
defer func() {
if r := recover(); r != nil {
log.Printf("Runtime error caught: %v", r)
}
}()
foo()
无论foo()
中是否触发了错误处理流程,对应的匿名defer函数都将在函数退出时得到执行。
假如foo()中触发了错误处理流程,recover()函数执行将使得该错误处理过程终止。
如果错误处理流程被触发时,程序传给panic函数的参数不为nil,则该函数还会打印详细的错误信息。