概念
在Go里面错误与异常是两个概念
错误是可以预期的,错误发生后,程序应该可以自行处理。
异常是不可预期的,异常的发生常常会导致程序崩溃。
关于错误
内建类型error
package main
import (
"fmt"
)
func main() {
a, err := testError(10, 0)
if err != nil {
fmt.Println(err.Error())
} else {
fmt.Println(a)
}
}
func testError(a int, b int) (result float32, err error) {
if b == 0 {
return 0, fmt.Errorf("fuck error")
}
return float32(a) / float32(b), nil
}
结果
fuck error
这是一个很经典的例子。这个例子利用的正是内建的error类型向外界传递函数执行出错的信息。
自定义错误类型
实际上内建类型error是一个接口,实现自定义错误其实只需要实现Error()string接口即可
package main
import (
"fmt"
)
func main() {
a, err := testError(10, 0)
if err != nil {
fmt.Println(err.Error())
} else {
fmt.Println(a)
}
}
func testError(a int, b int) (result float32, err error) {
if b == 0 {
return 0, err_mul{float32(a), float32(b)}
}
return float32(a) / float32(b), nil
}
type err_mul struct {
record_a float32
record_b float32
}
func (this err_mul) Error() string {
return fmt.Sprintf("fuck error %f/%f", this.record_a, this.record_b)
}
结果
fuck error 10.000000/0.000000
关于异常
异常不是错误,错误是程序员可预期的,程序员自然也可以处理的。异常是程序员无法预期的,往往会导致程序崩溃等不可恢复性错误。
关于defer关键字
package main
import "fmt"
func main() {
fmt.Println(testDefer(1))
fmt.Println(testDefer(0))
fmt.Println(testDefer(2))
}
func testDefer(a int) float32 {
fmt.Println("start")
defer fmt.Println("defer 1")
fmt.Println("run 1 step")
defer fmt.Println("defer 2")
b := 1 / a
defer fmt.Println("defer 3")
return float32(b)
}
结果:
start
run 1 step
defer 3
defer 2
defer 1
1
start
run 1 step
defer 2
defer 1
panic: runtime error: integer divide by zero
这种结果充分说明了defer关键字的作用:
1、defer只会在函数运行结束或者运行出错后才会运行。这两种情况可归纳为函数运行结束。即:
defer会在函数运行结束后开始运行
2、defer会从函数运行结束的地方开始倒序运行,这里的函数运行结束的地方可能是函数结尾,可能是函数异常退出的地方。即:
defer会从函数运行结束处倒序运行
defer机制通常用于关闭IO等操作。这样就可以避免IO异常情况下无法关闭而导致的错误。
关于panic内建函数
这是一个抛出异常的内建函数。异常通常来自运行时(例如做1/0的操作),但是程序员也可以手动抛出。
package main
import "fmt"
func main() {
testPanic()
}
func testPanic() {
defer fmt.Println("defer 1")
fmt.Println("run 1 step")
panic(fmt.Errorf("this is fuck error"))
fmt.Println("run 2 step")
}
结果
run 1 step
defer 1
panic: this is fuck error
可以看到,虽然做了defer善后,但是程序还是崩溃了。
通常,我们不应该在程序出现panic崩溃之后做任何处理,因为发程panic意味着程序出现了严重问题,最好让其强行终止。
但是我们也可以尝试拯救一下(虽然不建议这样做),这就需要用到recover
内建函数recover
recover可以处理panic,类似于try/cache中的cache
package main
import (
"fmt"
)
func main() {
fmt.Printf("result is %d\n", testPanic(0))
fmt.Printf("result is %d\n", testPanic(1))
}
func testPanic(a int) int {
defer func() {
rec := recover()
if rec != nil {
p := rec.(error)
fmt.Println("has error -> " + p.Error())
} else {
fmt.Println("no error")
}
}()
fmt.Println("run 1 step")
if a == 1 {
panic(fmt.Errorf("fuck error"))
}
fmt.Println("run 2 step")
return 1
}
结果:
run 1 step
run 2 step
no error
result is 1
run 1 step
has error -> fuck error
result is 0