9.1 error
Go 语言内置了一个简单的错误接口作为一种错误处理机制,接口定义如下:
type error interface {
Error() string
}
它包含了一个 Error()
方法,返回值为 string
Go 的 error 构造有两种方式,分别是
第一种:error.New()
err := errors.New("This is an error")
if err != nil {
fmt.Print(err)
}
第二种:fmt.Errorf()
err := erros.New("This is an error")
if err != nil {
fmt.Print(err)
}
除了直接使用 Go 自带的方法,还可以自定义错误。下面以自然数函数作为例子:
type NotNature float64
func (err NotNature) Error() string {
return fmt.Sprintf("自然数为大于或等于 0 的数: %v", float64(err))
}
func Nature(x float64) (float64, error) {
if x < 0 {
return 0, NotNature(x)
} else {
return x, nil
}
}
func main(){
fmt.Println(Nature(1))
fmt.Println(Nature(-1))
}
需要注意一下几点:
1.如果函数需要处理异常,通常将 error 作为多值返回的最后一个值,返回的 error 值为 nil 则表示无异常,非 nil 则是有异常。
2.一般先用if语句处理 error!=nil,正常逻辑放 if 后面。
Go 语言的 error 代表的并不是真“异常”,只是通过返回 error 来表示错误信息,换句话说,不是运行时错误范围预定义的错误,某种不符合期望的行为并不会导致程序无法运行(自然数函数例子),都应使用 error 进行异常处理。当程序出现重大错误,如数组越界,才会将其当成真正的异常,并用 panic 来处理。
9.2 panic
Go不使用try…catch方法来处理异常,而是使用panic和recover
先上代码举一个简单的例子
func main() {
fmt.Println("Hello,Go!")
panic(errors.New(" i am a error"))
fmt.Println("hello,again!")
}
可以看到,panic 后面的程序不会被执行了。但是我们捕捉异常并不是为了停止程序(一般情况),而是为了让程序能正常运行下去,这时候就到 recover 出场了。
package main
import "fmt"
func main(){
defer func(){
fmt.Println("我是defer里面第一个打印函数")
if err:=recover();err!=nil{
fmt.Println(err)
}
fmt.Println("我是defer里面第二个打印函数")
}()
f()
}
func f(){
fmt.Println("1")
panic("我是panic")
fmt.Println("2")
}
输出:
1
我是defer里面第一个打印函数
我是panic
我是defer里面第二个打印函数
可以看到,f 函数一开始正常打印,当遇到panic,就跳到 defer 函数,执行 defer 函数里的内容
需要注意的是,defer 函数里打印的err其实就是panic里面的内容。
下面详细介绍一下 panic 和 recover 的原理。首先来看一下 panic 和 recover 的官方定义
func panic(v interface{})
内置函数 panic 会停止当前 goroutine 的正常执行。当函数F调用 panic 时,F 的正常执行立即停止。任何被 F 延迟执行的函数都将以正常的方式运行,然后 F 返回其调用者。对调用方 G 来说,对 F 的调用就像调用 panic 一样,终止 G 的执行并运行任何延迟的函数。直到执行 goroutine 中的所有函数都按逆序停止。此时,程序将以非 0 退出代码终止。此终止序列称为 panicking,可由内置函数 recover 控制。
func recover() interface{}
recover 内置函数允许程序管理 panicking 的 goroutine 的行为。在 defer 函数(但不是它调用的任何函数)内执行恢复调用,通过恢复正常执行来停止panicking 序列,并检索传递给 panic 调用的错误值。如果在 defer 函数之外调用 recover,则不会停止 panicking 的序列。在这种情况下,或者当 goroutine 不 panicking 时,或者提供给 panic 的参数是 nil,recover 返回 nil。因此,recover 的返回值报告 goroutine 是否 panicking
ps:defer和recover必须在panic之前定义,否则无效。
9.3 源码分析
errors.New的定义如下
// src/errors/errors.go
// New returns an error that formats as the given text.
// Each call to New returns a distinct error value even if the text is identical.
func New(text string) error {
return &errorString{text}
}
// errorString is a trivial implementation of error.
type errorString struct {
s string
}
func (e *errorString) Error() string {
return e.s
}
-
New函数返回格式为给定文本的错误
-
即使文本是相同的,每次对New的调用都会返回一个不同的错误值。