Go 入门很简单:panic 和 recover
前言
panic
和 recover
可以被认为类似于其他语言(如 Java)中的 try-catch-finally 习惯用法,只是它们在 Go 中很少使用。
Go 中一个重要的点是我们应该尽可能避免使用 panic
和 revover
,可以多多使用 error
。只有在程序无法继续执行的情况下,才应该使用 panic
和 revover
机制。
使用 error 错误
处理 Go 程序中的异常情况的惯用方法是使用 error
。error
足以处理程序中出现的大多数异常情况。
但有些情况下,程序在出现异常情况后无法继续执行。在这种情况下,我们使用 panic
来提前终止程序。
当一个函数遇到 panic
时,它的执行被停止,任何延迟的函数被执行,然后控制权返回给它的调用者。这个过程一直持续到当前 goroutine
的所有函数都返回为止,这时程序会打印出 panic 信息,然后是堆栈跟踪,然后终止。
什么时候使用 panic
- 程序不能轻易地继续执行的不可恢复的错误。
一个示例是无法绑定到所需端口的 Web 服务器。在这种情况下,panic
是合理的,因为如果端口绑定失败,则后续操作无法进行下去。 - 程序员错误。
假设我们有一个接受指针作为参数的方法,并且有人使用 nil 参数调用此方法。在这种情况下,我们可能可以使用panic
,因为调用带有nil
参数的方法是程序员错误,该方法期望一个有效的指针。
panic 例子
首先我们看一下 panic 的内置方法:
func panic(interface{})
当程序终止时,传递给 panic 函数的参数会被打印。
我们来看一个例子:
package main
import (
"fmt"
)
func fullName(firstName *string, lastName *string) {
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)
fmt.Println("returned normally from fullName")
}
func main() {
firstName := "Ray"
fullName(&firstName, nil)
fmt.Println("returned normally from main")
}
运行会立马报错终止:
panic: runtime error: last name cannot be nil
goroutine 1 [running]:
main.fullName(0x405058?, 0xc000088f70?)
/home/wade/go/src/panicAndrevcover/panicRecover.go:12 +0x114
main.main()
/home/wade/go/src/panicAndrevcover/panicRecover.go:20 +0x35
exit status 2
让我们分析这个输出以了解 panic 是如何工作的,以及当程序 panic 时堆栈跟踪是如何打印的。
在第代码的 19 行 我们将 Ray 分配给 firstName。
我们在第 1 行调用 fullName 函数,lastName 为 nil。 第 20 行调用 fullName
函数时 lastname 为空 nil
。
因此第 11 行程序就会出现 panic,程序终止运行,这个参数传递给 panic 函数打印到终端。
然后时堆栈追踪,因为程序会在第 20 行的 panic 函数调用之后终止,代码中的 13, 14, 15 行不会被执行。
运行结果解释:
所以,这段程序首先会打印传递给 panic
函数的信息:
panic: runtime error: last name cannot be nil
然后打印堆栈追踪,程序在第 12 行的 fullname
函数调用 panic,所以输出:
goroutine 1 [running]:
main.fullName(0x405058?, 0xc000088f70?)
/home/wade/go/src/panicAndrevcover/panicRecover.go:12 +0x114
然后栈中的下一个元素会被打印,第 20 行调用的 fullname
,所以再输出:
/home/wade/go/src/panicAndrevcover/panicRecover.go:20 +0x35
exit status 2
现在我们已经到达了导致 panic 的顶层函数,之后没有更高层了,因此没有更多可以打印的内容。
另一个例子
panic 也可能是由运行时发生的错误引起的,例如尝试访问切片中不存在的索引。
package main
import (
"fmt"
)
func slicePanic() {
n := []int{5, 7, 4}
fmt.Println(n[4])
fmt.Println("normally returned from a")
}
func main() {
slicePanic()
fmt.Println("normally returned from main")
}
在上面的程序中,第 9 行正在尝试访问 n[4] ,它是切片中的无效索引。该程序将出现以下输出 panic:
panic: runtime error: index out of range [4] with length 3
goroutine 1 [running]:
main.slicePanic()
/home/wade/go/src/panicAndrevcover/panicRecover.go:9 +0x1d
main.main()
/home/wade/go/src/panicAndrevcover/panicRecover.go:13 +0x19
exit status 2
文章来源:
- 点赞
- 收藏
- 关注作者
评论(0)