GO语言的错误处理

前言

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

 

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

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

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

type error interface { 
    Error() string 
}

错误定义

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

type NullException struct {
}
​
func (ne *NullException) Error() string {
    return "NullPointException"
}
​
func Trim(s *string) (string, error) {
    if s == nil {
        return "", &NullException{}
    } else {
        return strings.TrimSpace(*s), nil
    }
}

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

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

func Trim(s *string) (string, error) {
    if s == nil {
        return "", errors.New("NullPointException")
    } else {
        return strings.TrimSpace(*s), nil
    }
}

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

func TestException(t *testing.T) {
   var s *string
​
   if rtn, err := Trim(s); err == nil {
      fmt.Printf("Trim(%v)=%v \n", s, rtn)
   } else {
      fmt.Printf("Trim(%v) throw exception: %v \n", s, err)
   }
​
   var a string
   if rtn, err := Trim(&a); err == nil {
      fmt.Printf("Trim(%v)=%v \n", &a, rtn)
   } else {
      fmt.Printf("Trim(%v) throw exception: %v \n", &a, err)
   }
}
​
​
===== OUTPUT =====
=== RUN   TestException
Trim(<nil>) throw exception: NullPointException 
Trim(0xc000273a10)= 
--- PASS: TestException (0.00s)
PASS

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

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

错误的判断

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

来看看下面的代码

type NotFoundException struct {
}
​
func (ne NotFoundException) Error() string {
    return "NotFoundException"
}
​
type NotAllowException struct {
}
​
func (ne NotAllowException) Error() string {
    return "NotAllowException"
}
​
func ReadFile(path string) ([]byte, error) {
    n := len(path)
    if n <= 1 {
        return nil, NotFoundException{}
    } else if n <= 12 {
        return nil, NotAllowException{}
    }
​
    return []byte(path), nil
}
​
func ReadOne(path string) {
    if rtn, err := ReadFile(path); err == nil {
        fmt.Printf("ReadFile(%v)=%v \n", path, rtn)
    } else {
        switch err.(type) {
        case NotFoundException:
            fmt.Printf("ReadFile(%v) throw NotFoundException: %v \n", path, err)
        case NotAllowException:
            fmt.Printf("ReadFile(%v) throw NotAllowException: %v \n", path, err)
        default:
            fmt.Printf("ReadFile(%v) throw UnknowException: %v \n", path, err)
        }
    }
}

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

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

func TestException2(t *testing.T) {
    var s string
​
    s = "/"
    ReadOne(s)
​
    s = "/readme.txt"
    ReadOne(s)
​
    s = "/home/readme.txt"
    ReadOne(s)
}
​
===== OUTPUT =====
=== RUN   TestException2
ReadFile(/) throw NotFoundException: NotFoundException 
ReadFile(/readme.txt) throw NotAllowException: NotAllowException 
ReadFile(/home/readme.txt)=[47 104 111 109 101 47 114 101 97 100 109 101 46 116 120 116] 
--- PASS: TestException2 (0.00s)
PASS

 

 

结束语

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

inthirties

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值