更喜岷山千里雪,三军过后尽开颜
初学编程时总认为编程工作就是一堆杂乱无章的英文字符,既无生命力,也无成就感,这样的时间大概延续了一年左右的时间,中途好多次都曾经放弃,不过看着周围的其他人都在坚持学习,最终还是硬着头皮选择坚持了下来。其实从过往者的经历来讲,对于绝大部分的普通编程学习者而言,一年的时间确实是一个长久的考验期,坚持的下来就可以步入编程的大门,坚持不下来可能最终会功亏一篑。无论是单纯的控制台的输出还是简单Demo的搭建,最重要的是不断的坚持,并从一个个问题的解决中获取内心的成就感, 唯有坚持方能领略:“更喜岷山千里雪,三军过后尽开颜”喜悦。
go语言的错误处理
1、什么是错误?
所谓错误是指程序的运行的过程中出现了异常, 例如程序打开一个文件时,如果文件不存在,那么程序就会出现错误。
//举个栗子
func main() {
f, err := os.Open("/test.txt")
if err != nil {
fmt.Println(err)
return
}
fmt.Println(f.Name(), "opened successfully")
}
2、使用New函数自定义错误
//举个栗子
func circleArea(radius float64) (float64, error) {
if radius < 0 {
return 0, errors.New("Area calculation failed, radius is less than zero")
}
return math.Pi * radius * radius, nil
}
3、使用结构体定义错误
//举个栗子
type areaError struct {
err string //error description
length float64 //length which caused the error
width float64 //width which caused the error
}
func (e *areaError) Error() string {
return e.err
}
func (e *areaError) lengthNegative() bool {
return e.length < 0
}
func (e *areaError) widthNegative() bool {
return e.width < 0
}
func rectArea(length, width float64) (float64, error) {
err := ""
if length < 0 {
err += "length is less than zero"
}
if width < 0 {
if err == "" {
err = "width is less than zero"
} else {
err += ", width is less than zero"
}
}
if err != "" {
return 0, &areaError{err, length, width}
}
return length * width, nil
}
func main() {
length, width := -5.0, -9.0
area, err := rectArea(length, width)
if err != nil {
if err, ok := err.(*areaError); ok {
if err.lengthNegative() {
fmt.Printf("error: length %0.2f is less than zero\n", err.length)
}
if err.widthNegative() {
fmt.Printf("error: width %0.2f is less than zero\n", err.width)
}
return
}
fmt.Println(err)
return
}
fmt.Println("area of rect", area)
}
panic
1、什么是panic?
panic是go语言的一种错误的处理机制,通常用于程序发生异常,无法正常处理的情形下我们会使用panic来终止程序的执行。
常用的使用场景:发生了一个不能恢复的错误,此时程序不能正常的执行;发生了编程上的错误(比如应该用指针接收的变量,调用的时候却使用nil进行调用)
//举个栗子
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 := "Elon"
fullName(&firstName, nil)
fmt.Println("returned normally from main")
}
2、panic执行的过程中含有defer语句如何执行
执行的顺序是:按照栈的执行顺序先执行完defer语句,之后才执行panic语句
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)
fmt.Println("returned normally from fullName")
}
func main() {
defer fmt.Println("deferred call in main")
firstName := "Elon"
fullName(&firstName, nil)
fmt.Println("returned normally from main")
}
//执行结果
deferred call in fullName
deferred call in main
panic: runtime error: last name cannot be nil
goroutine 1 [running]:
main.fullName(0x1042bf90, 0x0)
/tmp/sandbox060731990/main.go:13 +0x280
main.main()
/tmp/sandbox060731990/main.go:22 +0xc0
recover
1、什么是recover?
recover是一个内建的函数,用于重新获取对panic的控制,只有在延迟函数的内调用才有用。在延迟函数内调用 recover,可以取到 panic 的错误信息,并且停止 panic 续发事件(Panicking Sequence),程序运行恢复正常。如果在延迟函数的外部调用 recover,就不能停止 panic 续发事件。
//举个栗子
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)
fmt.Println("returned normally from fullName")
}
func main() {
defer fmt.Println("deferred call in main")
firstName := "Elon"
fullName(&firstName, nil)
fmt.Println("returned normally from main")
}
2、panic、recover和go协程
当panic和recover和go协程结合使用的时候,只有在相同的协程中调用recover才管用,recover不能恢复一个不同协程的panic
//举个栗子
func recovery() {
if r := recover(); r != nil {
fmt.Println("recovered:", r)
}
}
func a() {
defer recovery()
fmt.Println("Inside A")
go b()
time.Sleep(1 * time.Second)
}
func b() {
fmt.Println("Inside B")
panic("oh! B panicked")
}
func main() {
a()
fmt.Println("normally returned from main")
}
总结
不同编程语言处理异常的方式有所区别,但是核心的思想都大同小异,好的程序必然少不了对错误的预防处理,借用某位大佬的经典名言:程序员日常的工作不是在写bug,就是在解决bug的路上。