Go-错误处理

目录

一、Go的错误处理机制

1、Go有类似于 try{}cache{} 一样的错误处理机制吗?

2、与其它主要编程语言的差异

3、error 接口

4、错误处理示例

5、预制错误

6、总结

二、os.Exit、panic 和 recover

1、os.Exit

2、panic

3、recover

4、总结


一、Go的错误处理机制

1、Go有类似于 try{}cache{} 一样的错误处理机制吗?

回答没有

这个Go的三位创始人是有关系的,他们坚持认为程序员对于异常的随意使用,包括try{}cache{}finally{} 的结构对于代码的影响会很大,他们更喜欢 C 的这种对于返回值的判断。

另一个理由就是 Go 支持了多返回值的结构,对于第二返回值的判断可以比较方便的处理错误。

2、与其它主要编程语言的差异

  • 没有异常机制
  • error 类型实现了 error interface
  • 可以公共 errors.New() 来快速创建错误实例

3、error 接口

type error interface {
	Error() string
}

4、错误处理示例

场景:我们以获取斐波拉西数列为例。

package error

import (
	"errors"
	"testing"
)

//获取斐波拉西数列
func GetFibonacci(num int) ([]int, error)  {
	//通过 errors.new() 方法创建错误并返回给调用者
	if num < 2 {
		return nil, errors.New("num should be greater than 2")
	}

	fibList := []int{1, 1}

	for i := 2; i < num ; i++ {
		fibList = append(fibList, fibList[i-2] + fibList[i-1])
	}

	return fibList, nil
}

//调用 GetFibonacci() 方法,并对错误进行处理
func TestFibonacci(t *testing.T)  {
	var (
		err error
		list []int
	)

    //及早失败,避免嵌套
	//我们最好是先判断 err != nil,如果有错误尽早输出错误信息,然后return,
	//这样做可以防止 if 语句嵌套的太深,增加代码的可读性
	if list, err = GetFibonacci(-10); err != nil {
		t.Log(err)
		return
	}

	//只有当没有任何错误的时候,最后输出结果
	t.Log(list)
}

5、预制错误

package error

import (
	"errors"
	"fmt"
	"testing"
)

//在 package 里面定义两个预制的错误,方便做字符串的比较判断
var LessThanTwoError = errors.New("num should be greater than 2")
var GreaterThanOneHundredError = errors.New("num should be less than 100")

//获取斐波拉西数列
func GetFibonacci(num int) ([]int, error)  {
	//通过 errors.new() 方法创建错误并返回给调用者
	if num < 2 {
		return nil, LessThanTwoError
	}

	if num > 100 {
		return nil, GreaterThanOneHundredError
	}

	fibList := []int{1, 1}

	for i := 2; i < num ; i++ {
		fibList = append(fibList, fibList[i-2] + fibList[i-1])
	}

	return fibList, nil
}

//调用 GetFibonacci() 方法,并对错误进行处理
func TestFibonacci(t *testing.T)  {
	var (
		err error
		list []int
	)

	//及早失败,避免嵌套
	//我们最好是先判断 err != nil,如果有错误尽早输出错误信息,然后return,
	//这样做可以防止 if 语句嵌套的太深,增加代码的可读性
	if list, err = GetFibonacci(-10); err != nil {
		//通过预制错误来进行判断,调用者在判断错误时,相对来说会比较简单
		if err == LessThanTwoError {
			fmt.Println("It's less than 2")
			num := 3
			t.Log(num)
		}
		return
	}

	//只有当没有任何错误的时候,最后输出结果
	t.Log(list)
}

6、总结

  • Go不支持异常机制,异常处理是通过 errors interface 来进行返回,然后让调用者进行判断;
  • 通过 errors.new() 方法创建错误;
  • 调用者处理异常时,要遵循及早失败,避免嵌套的原则;
  • 调用者如果需要做错误判断,最好用预制错误的方式,比起用字符串判断,更加不易出错。

二、os.Exit、panic 和 recover

1、os.Exit

  • os.Exit退出时不会调用 defer 指定的函数;
  • os.Exit 退出时不输出当前调用栈信息。
//调试 os.Exit() 函数的效果
func TestOsExit(t *testing.T) {
	defer func() {
		fmt.Println("This is defer function")
	}()

	fmt.Println("start")
	os.Exit(-1)  // Process finished with exit code 1
	fmt.Println("end")
}

运行程序,我们会发现 os.Exit() 不会调用 defer() 函数,也没有调用栈的信息,只是输出了一个错误码。

2、panic

  • panic用于不可恢复的错误;
  • panic退出前会执行 deffer 函数中的内容。
package panic_recover

import (
	"errors"
	"fmt"
	"os"
	"testing"
)

//调试 panic() 函数的效果
func TestPanic(t *testing.T) {
	defer func() {
		fmt.Println("This is defer function")
	}()

	fmt.Println("start")
	panic(errors.New("something wrong"))
	fmt.Println("end")
}

/*
start
This is defer function
--- FAIL: TestPanic (0.00s)
panic: something wrong [recovered]
panic: something wrong

goroutine 6 [running]:
testing.tRunner.func1.1(0x1120560, 0xc000052520)
/usr/local/go/src/testing/testing.go:1076 +0x30d
testing.tRunner.func1(0xc000001380)
*/

运行函数,我们发现 panic 会将调用栈也打印出来,而且会调用 defer 函数中的内容。

3、recover

package panic_recover

import (
	"errors"
	"fmt"
	"testing"
)

//调试 recover() 函数的效果
func TestRecover(t *testing.T) {
	defer func() {
		//当抛出了 panic() 错误,我们又不希望程序中断和退出,
		//可以通过 recover() 来修复错误。
		if err := recover(); err != nil {
			//修复错误
			t.Log("通过 recover 来修复错误")
		}
	}()

	fmt.Println("start")
	panic(errors.New("something wrong"))
	fmt.Println("end")
}

/*
start
panic_recover_test.go:19: 通过 recover 来修复错误
--- PASS: TestRecover (0.00s)
PASS
*/

注意 recover 的使用,最好不要在 recover() 中只记录一个日志或者忽略掉这个错误,然后什么都不做,比如如果是系统资源消耗完了,此时 recover 进行强制恢复,这样子会让我们的健康检查程序没办法检查出当前系统的问题,health check 只会检查当前的进程是在还是不在,这样就形成了僵尸服务进程。在这种情况下,我们最好使用一种可恢复的设计模式,例如 Let it crash,我们干脆让程序 crash 掉,然后让守护进程帮我们把程序重新启动起来,这往往是我们回复不确定错误的最好方法。

4、总结

  • os.Exit退出时不会调用 defer() 指定的函数;
  • os.Exit 退出时输出当前调用栈信息。
  • panic用于不可恢复的错误;
  • panic退出前会执行 deffer() 函数中的内容。
  • 当抛出了 panic() 错误,我们又不希望程序中断和退出,可以通过 recover() 来修复错误。

:这篇博文是我学习中的总结,如有转载请注明出处:

https://blog.csdn.net/DaiChuanrong/article/details/118118967

上一篇Go面向对象-拓展和多肽

下一篇Go-package

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值