golang学习笔记八:error panic 和 recover

error

error的基础接口:

1
2
3
type error interface {
Error() string
}

当然也就可以套一个复杂一点的壳子,比如os.PathError

1
2
3
4
5
6
7
8
9
10
11
// PathError records an error and the operation and
// file path that caused it.
type PathError struct {
Op string // "open", "unlink", etc.
Path string // The associated file.
Err error // Returned by the system call.
}

func (e *PathError) Error() string {
return e.Op + " " + e.Path + ": " + e.Err.Error()
}

PathError‘s Error generates a string like this:

1
open /etc/passwx: no such file or directory

这个思路就很简单,如果捕捉到有error,就可以执行一些比如回滚之类的代码来恢复或者重新执行。

panic

当然也有一些错是希望直接抛出并且停止执行的(怎么那么像throw)

1
2
3
4
5
6
7
8
9
10
11
12
13
// A toy implementation of cube root using Newton's method.
func CubeRoot(x float64) float64 {
z := x/3 // Arbitrary initial value
for i := 0; i < 1e6; i++ {
prevz := z
z -= (z*z*z-x) / (3*z*z)
if veryClose(z, prevz) {
return z
}
}
// A million iterations has not converged; something is wrong.
panic(fmt.Sprintf("CubeRoot(%g) did not converge", x))
}

当然panic也像throw一样是不被提倡的,比起抛出并且停止程序倒不如好好走完(不然也不知道会出什么问题)。

recover

当函数发生 panic 时,它会终止运行,在执行完所有的延迟函数后,程序控制返回到该函数的调用方。这样的过程会一直持续下去,直到当前协程的所有函数都返回退出,然后程序会打印出 panic 信息,接着打印出堆栈跟踪,最后程序终止

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func fullName(firstName *string, lastName *string) {  
defer fmt.Println("deferred call in fullName")
if firstName == nil {
panic("runtime error: first name cannot be nil")
}
if lastName == nil {
panic("runtime error: last name cannot be nil")
}
fmt.Printf("%s %s\n", *firstName, *lastName)
}

func main() {
defer fmt.Println("deferred call in main")
firstName := "Elon"
fullName(&firstName, nil)
fmt.Println("returned normally from main")
}

两个函数里面的defer就是延迟函数,所以遇到panic之后的输出会变成:”defer call in fullName” -> “deferred call in main”->”runtime error”,利用先执行延迟函数的特征,recover出现了。在延迟函数内调用 recover,可以取到 panic 的错误信息,并且停止 panic 续发事件(Panicking Sequence),程序运行恢复正常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
func recoverName() {  
if r := recover(); r!= nil {
fmt.Println("recovered from ", r)
}
}

func fullName(firstName *string, lastName *string) {
defer recoverName()
if firstName == nil {
panic("runtime error: first name cannot be nil")
}
if lastName == nil {
panic("runtime error: last name cannot be nil")
}
fmt.Printf("%s %s\n", *firstName, *lastName)
}

func main() {
defer fmt.Println("deferred call in main")
firstName := "Elon"
fullName(&firstName, nil)
fmt.Println("returned normally from main")
}

上面的程序的输出流程就会变成:”recovered from” -> “returned normally from main” -> “deferred call in main”


到此为止effective go 这本书就算看完了,个人认为不算是一本绝佳的入门教程,但是胜在短小并且覆盖的面还蛮全面的,学到这个份儿上还不自己撸代码就有点对不起这段时间的学习了,后面的目标就是边学go的web框架边给自己定个奇奇怪怪的小目标写个实例代码吧(就聊天室吧)