错误处理是任何语言都需要考虑到的问题,而 Go 语言在错误处理上解决得更为完善,优雅的错误处理机制是 Go 语言的一大特点。
error
Go 语言引入了一个错误处理的标准模式,即 error 接口,该接口定义如下:
type error interface {
Error() string
}
对于大多数函数,如果要返回错误,可以将 error 作为多返回值的最后一个:
func foo(param int)(ret int, err error)
{
...
}
调用时的代码:
n, err := foo(0)
if err != nil {
// 错误处理
} else {
// 使用返回值n
}
我们还可以自定义错误类型,创建源文件 error.go,输入以下代码:
package main
import "fmt"
import "errors"
//自定义的出错结构
type myError struct {
arg int
errMsg string
}
//实现Error接口
func (e *myError) Error() string {
return fmt.Sprintf("%d - %s", e.arg, e.errMsg)
}
//两种出错
func error_test(arg int) (int, error) {
if arg < 0 {
return -1, errors.New("Bad Arguments - negtive!")
}else if arg >256 {
return -1, &myError{arg, "Bad Arguments - too large!"}
}
return arg*arg, nil
}
//相关的测试
func main() {
for _, i := range []int{-1, 4, 1000} {
if r, e := error_test(i); e != nil {
fmt.Println("failed:", e)
} else {
fmt.Println("success:", r)
}
}
}
defer
你可以在 Go 函数中添加多个 defer 语句,当函数执行到最后时,这些 defer 语句会按照逆序执行(即最后一个 defer 语句将最先执行),最后该函数返回。特别是当你在进行一些打开资源的操作时,遇到错误需要提前返回,在返回前你需要关闭相应的资源,不然很容易造成资源泄露等问题。如下代码所示,我们一般写打开一个资源是这样操作的:
func CopyFile(dst, src string) (w int64, err error) {
srcFile, err := os.Open(src)
if err != nil {
return
}
defer srcFile.Close()
dstFile, err := os.Create(dst)
if err != nil {
return
}
defer dstFile.Close()
return io.Copy(dstFile, srcFile)
}
如果 defer 后面一条语句干不完清理工作,也可以使用一个匿名函数:
defer func(){
...
}()
注意,defer 语句是在 return 之后执行的,新建源文件 defer.go输入以下代码:
func test() (result int) {
defer func() {
result = 12
}()
return 10
}
func main() {
fmt.Println(test()) // 12
}
panic 和 recover
panic() 函数用于抛出异常,recover() 函数用于捕获异常,这两个函数的原型如下:
func panic(interface{})
func recover() interface{}
当在一个函数中调用 panic() 时,正常的函数执行流程将立即终止,但函数中之前使用 defer 关键字延迟执行的语句将正常展开执行,之后该函数将返回到调用函数,并导致逐层向上执行 panic() 流程,直至所属的 goroutine 中所有正在执行的函数被终止。错误信息将被报告,包括在调用 panic() 函数时传入的参数,这个过程称为错误流程处理。
panic() 接受一个 interface{} 参数,可支持任意类型,例如:
panic(404)
panic("network broken")
panic(Error("file not exists"))
在 defer 语句中,可以使用 recover() 终止错误处理流程,这样可以避免异常向上传递,但要注意 recover() 之后,程序不会再回到 panic() 那里,函数仍在 defer 之后返回。新建一个源文件 error1.go,输入以下代码:
func foo() {
panic(errors.New("i'm a bug"))
return
}
func test() (result int) {
defer func() {
if r := recover(); r != nil {
err := r.(error)
fmt.Println("Cache Exception:", err)
}
}()
foo()
return 10
}
func main() {
fmt.Println(test()) // 0
}