• GO语言的错误处理


    前言

    如介绍函数章节里所说的,在Go语言里,没有try...catch这样的异常处理机制,不能像java那样执行抛异常操作, 但是在Go语言里可以使用defer...recover...panic的机制来实现类似try...catch的效果;今天我们就专门来探讨这个话题;

     

    Go里为什么不设计try...catch异常机制

    java里使用try/catch 机制,在函数定义声明Expception的使用层次上不够简洁,调用时对异常的处理太泛滥,并且使用内存栈空间从底层向更高的层级抛异常太耗费资源。Go设计的机制没有使用java里try...catch类似的异常机制,但是也实现了 “捕捉” 异常放入机制,但是更轻量,且只作为(处理错误的)最后的手段。

    GO里预定义了一个error接口类型;错误值用来表示错误状态, 错误类型也是种数据类型,和其他数据类型一样,可以作为参数也可以作为返回值

    1. type error interface {
    2. Error() string
    3. }

    错误定义

    在GO语言里,没有类似Java那么强的面向对象的特性, 可以通过结构体实现Error方法,来自定义一个错误对象类型;例如

    1. type NullException struct {
    2. }
    3. func (ne *NullException) Error() string {
    4. return "NullPointException"
    5. }
    6. func Trim(s *string) (string, error) {
    7. if s == nil {
    8. return "", &NullException{}
    9. } else {
    10. return strings.TrimSpace(*s), nil
    11. }
    12. }

    如上述代码,就自行定义了一个异常对象类型NullException; 可以通过 err:=&NullException{}生成一个错误对象;

    上面是通过type struct来自定义错误; 除了这种方式,还可以使用errors包里的内置函数errors.New来产生一个新的错误类型对象;如下面代码:

    1. func Trim(s *string) (string, error) {
    2. if s == nil {
    3. return "", errors.New("NullPointException")
    4. } else {
    5. return strings.TrimSpace(*s), nil
    6. }
    7. }

    这种方式相对于上面的方式来说,代码上更简单了,在很多的源代码里都是类似这样的写法; 下面来看看一个完整的实例

    1. func TestException(t *testing.T) {
    2.   var s *string
    3.   if rtn, err := Trim(s); err == nil {
    4.      fmt.Printf("Trim(%v)=%v \n", s, rtn)
    5.   } else {
    6.      fmt.Printf("Trim(%v) throw exception: %v \n", s, err)
    7.   }
    8.   var a string
    9.   if rtn, err := Trim(&a); err == nil {
    10.      fmt.Printf("Trim(%v)=%v \n", &a, rtn)
    11.   } else {
    12.      fmt.Printf("Trim(%v) throw exception: %v \n", &a, err)
    13.   }
    14. }
    15. ===== OUTPUT =====
    16. === RUN   TestException
    17. Trim(<nil>) throw exception: NullPointException
    18. Trim(0xc000273a10)=
    19. --- PASS: TestException (0.00s)
    20. PASS

    在上面的代码中,第一次调用Trim(s),s定义为string类型的指针,对于指针类型的变量,定义后自动初始化为nil,所以Trim(s)调用会返回NullExpcetion对象; 也就打印出Trim() throw exception: NullPointException ;

    对于第二个Trim(&a);a定义为string类型对象,string基础类型对象,定义后自动初始化为空字符串(""),所以Trim(&a)调用,传入的string指针不为空nil(而是空字符串""变量的指针),此时返回的错误对象是nil,没有错误;返回的是空字串串Trim以后的结果,还是空字符串;也就打印出Trim(0xc000273a10)=

    错误的判断

    有的时候在函数处理的过程中,可以会有不同类型的错误条件发生;比如对于文件处理的场景来说;有时候可能是文件路径错误,有的时候可能是文件的状态错误;当多种错误发生的时候,对错误的判断就有使用场景的价值了。使用类型断言或类型判断(type-switch)是处理这种场景非常高效的方法,并且可以 根据错误场景做一些补救和恢复操作。

    来看看下面的代码

    1. type NotFoundException struct {
    2. }
    3. func (ne NotFoundException) Error() string {
    4. return "NotFoundException"
    5. }
    6. type NotAllowException struct {
    7. }
    8. func (ne NotAllowException) Error() string {
    9. return "NotAllowException"
    10. }
    11. func ReadFile(path string) ([]byte, error) {
    12. n := len(path)
    13. if n <= 1 {
    14. return nil, NotFoundException{}
    15. } else if n <= 12 {
    16. return nil, NotAllowException{}
    17. }
    18. return []byte(path), nil
    19. }
    20. func ReadOne(path string) {
    21. if rtn, err := ReadFile(path); err == nil {
    22. fmt.Printf("ReadFile(%v)=%v \n", path, rtn)
    23. } else {
    24. switch err.(type) {
    25. case NotFoundException:
    26. fmt.Printf("ReadFile(%v) throw NotFoundException: %v \n", path, err)
    27. case NotAllowException:
    28. fmt.Printf("ReadFile(%v) throw NotAllowException: %v \n", path, err)
    29. default:
    30. fmt.Printf("ReadFile(%v) throw UnknowException: %v \n", path, err)
    31. }
    32. }
    33. }

    上面的代码ReadOne函数里,就有错误判断的处理方法; 在ReadOne函数里调用ReadFile函数,在ReadFile函数里简单的实现了,如果path长度不大于1,返回错误NotFoundException;如果path长度不大于12;返回错误NotAllowException;其他的就正常返回;

    ReadFile函数错误类型就可能返回多种类型;ReadOne函数就是通过type-swtich的方式,对err对象的类型进行判断; 如果是NotFoundException就执行一段逻辑;如果是NotAllowException错误类型就执行另一端逻辑;如果两种错误类型都没有匹配上就执行第三段逻辑; 错误类型NotFoundException和NotAllowException都在ReadOne函数上方进行了定义来一个现实的调用代码:

    1. func TestException2(t *testing.T) {
    2. var s string
    3. s = "/"
    4. ReadOne(s)
    5. s = "/readme.txt"
    6. ReadOne(s)
    7. s = "/home/readme.txt"
    8. ReadOne(s)
    9. }
    10. ===== OUTPUT =====
    11. === RUN   TestException2
    12. ReadFile(/) throw NotFoundException: NotFoundException
    13. ReadFile(/readme.txt) throw NotAllowException: NotAllowException
    14. ReadFile(/home/readme.txt)=[47 104 111 109 101 47 114 101 97 100 109 101 46 116 120 116]
    15. --- PASS: TestException2 (0.00s)
    16. PASS

     

     

    结束语

    错误处理是每种编程语言里需要面对的问题,也是编程过程中必须考虑的一个问题;错误处理如果做的好的话,代码的稳定性会很好。今天的这个文章通过代码实例给大家介绍了GO语言里有关错误处理的一些学问;大家也多用代码练习一下,写成更稳定的程序。

    欢迎大家继续关注 GO语言编程训练

  • 相关阅读:
    【芯片前端】延迟一拍出数的握手型ram结构的一次探索
    奔驰EQS450升级小柏林音响是什么样的体验
    基于JavaSwing开发中国跳棋游戏带论文 课程设计 大作业 毕业设计
    C++11智能指针weak_ptr
    【洛谷算法题】P1425-小鱼的游泳时间【入门1顺序结构】
    基于数据驱动的变电站巡检机器人自抗扰控制
    Docker:Gitlab 安装
    【Matplotlib绘制图像大全】(三):水平柱状图
    详细sqli-labs(1-65)通关讲解
    SQL每日一练(牛客新题库)——第6天:必会常用函数
  • 原文地址:https://blog.csdn.net/inthirties/article/details/126639780