go的错误机制
- golang没有异常机制
- error类型实现了异常接口
type error interface {
Error() string
}
- 可以通过errors.New快速创建错误实例
errors.New("n must be ddxfsefsdf")
废话不多说, 来了解下go兼并python的优雅
import (
"errors"
"testing"
)
func GetFebnacci(n int) ([]int, error) {
if n < 2 || n >= 100 {
return nil, errors.New("n should be in [2, 100]")
}
fiblist := []int{1, 1}
for i := 2; i < n; i++ {
fiblist = append(fiblist, fiblist[i-2]+fiblist[i-1])
}
return fiblist, nil
}
func TestGetFebnacci(t *testing.T) {
//t.Log(GetFebnacci(10))
if v,err:=GetFebnacci(100);err!=nil{
t.Error(err)
}else {
t.Log(v)
}
}
最后输出:
=== RUN TestGetFebnacci
error_test.go:24: n should be in [2, 100]
--- FAIL: TestGetFebnacci (0.00s)
FAIL
注意下写法
自定义错误
我们为了开发时候能更清楚是哪里报错了,且封装一些信息出去,我们更倾向于自定义一些错误类型进行输出error msg 不多说: 上代码
var LessThanTwoError = errors.New("n should not less than 2")
var LargeThanHundredError = errors.New("n should not large than 100")
那么错误判断可以修改:
func GetFebnacci(n int) ([]int, error) {
if n < 2 {
return nil, LessThanTwoError
}
if n >= 100 {
return nil, LargeThanHundredError
}
fiblist := []int{1, 1}
for i := 2; i < n; i++ {
fiblist = append(fiblist, fiblist[i-2]+fiblist[i-1])
}
return fiblist, nil
}
- go倾向于快速报错,将容易出错的放在最前面 测试:
func TestGetFebnacci(t *testing.T) {
//t.Log(GetFebnacci(10))
if v, err := GetFebnacci(100); err != nil {
if err == LessThanTwoError{
fmt.Println("it is less")
}
t.Error(err)
} else {
t.Log(v)
}
}
最佳实践
其实错误处理的最佳实践就是上面的,倾向于用自定义类型去处理,以便于判断错误类型
dosth returnpanic和recover
panic
- panic:用于不可恢复的错误
- panic:退出前会执行defer指定的内容
os.Exit()- os比较牛逼,退出前是不会执行defer的内容的
- os.Exit退出时候,不会输出当前调用栈信息
看代码:
func TestPanicVsExit(t *testing.T) {
fmt.Println("start")
os.Exit(-1)
}
这段代码输出:
=== RUN TestPanicVsExit
start
Process finished with the exit code 1
panic接收一个空接口,通常我们会传递一个错误进去:
func TestPanicVsExit(t *testing.T) {
fmt.Println("start")
panic(errors.New("something wrong"))
}
输出:
=== RUN TestPanicVsExit
start
--- FAIL: TestPanicVsExit (0.00s)
panic: something wrong [recovered]
panic: something wrong
// 省略 go routine 的错误栈!!
- 你运行下就知道,panic是会输出调用栈的信息的
- os.Exit不会,啥都没
加上defer函数
func TestPanicVsExit(t *testing.T) {
defer func() {
fmt.Println("Finally!")
}()
fmt.Println("start")
panic(errors.New("something wrong"))
}
看下输出:
=== RUN TestPanicVsExit
start
Finally!
--- FAIL: TestPanicVsExit (0.00s)
panic: something wrong [recovered]
panic: something wrong
goroutine 6 [running]:
// 省略调用栈的信息
- 看到了吧~ defer函数是会在退出前执行的
try...catch...defer func(){
if err:=recover();err!=nil{
// 恢复错误
}
}()
上代码demo吧:
func TestPanicVsExit(t *testing.T) {
defer func() {
if err:=recover(); err != nil{
fmt.Println("recover from:", err)
}
}()
fmt.Println("start")
//os.Exit(-1)
panic(errors.New("something wrong"))
}
输出是什么呢?
=== RUN TestPanicVsExit
start
recover from: something wrong
--- PASS: TestPanicVsExit (0.00s)
PASS
Process finished with the exit code 0
没有再报错了:pass 了 那我们可以说: panic被recover了(恐慌痛苦被恢复了? 怪怪的)
但是:方才不是说catch是不好?会出现什么问题?
- 容易导致僵尸服务进程,导致heal check失效
- let it crash ,让它报错,往往是我们恢复不确定性错误的最好方法