GO-异常处理

异常简介

一、异常分类

1.1、Panic

1、GO中Panic是一种系统级别的异常,一旦出现回终端程序的运行,来自运行时。并且当我们手动调用panic()函数的时候也会触发这个异常。用于严重错误,此时不管出现在哪里Panic都会直接结束程序(除非进行恢复)

2、手动触发panic()如下:

func main(){
	//do something
	panic("panic happend !! ")
	//do sonmething will not be excute
}

panic()触发的时候,会立即在当前GO程中执行一个 defer1 ,并且其实际会输出详细的堆栈调用日志。

1.2、Error

1.2.1、errors.go

1、Error 在GO中默认使用返回值的方法进行返回,要不你接受这个错误进行处理,要不使用_匿名接收这个错误不处理。

  • nil:表示没有错误

2、手动抛出一个error

func main(){
	throwError()
}

func throwError()  (err error ){
	//do something
	return errors.New("this is a new error")
}

3、errors
1、errors源码如下:

package errors  
  
// New returns an error that formats as the given text.
// Each call to New returns a distinct error value even if the text is identical.
func New(text string) error {  
   return &errorString{text}  
}
  
// errorString is a trivial implementation of error.
type errorString struct {  
   s string  
}

// output error detail 
func (e *errorString) Error() string {  
   return e.s  
}

根据errors.go我们可以按照他的方法自定义一个错误类型并且实现错误error接口,实现自定义错误类型。

// error 接口
type error interface {  
   Error() string  
}

官方实例代码:

package errors_test  
  
import (  
   "fmt"  
   "time")  
  
// MyError is an error implementation that includes a time and message.  
type MyError struct {  
   When time.Time  
   What string  
}  
  
func (e MyError) Error() string {  
   return fmt.Sprintf("%v: %v", e.When, e.What)  
}  
  
func oops() error {  
   return MyError{  
      time.Date(1989, 3, 15, 22, 30, 0, 0, time.UTC),  
      "the file system has gone away",  
   }}  
  
func Example() {  
   if err := oops(); err != nil {  
      fmt.Println(err)  
   }   // Output: 1989-03-15 22:30:00 +0000 UTC: the file system has gone away  
}

1.2.2、warp.go

参考:

  1. 深入Go:错误的包装与解包 - 腾讯云开发者社区-腾讯云 (tencent.com)
  2. Go 编程: 错误的诞生与死亡:包装、检查与处理 | 码农网 (codercto.com)

实际上包裹了一个error错误链,具体原理如下:

1.2.2.1、Unwrap(err error) error

Unwrap(err error) error :获取内层的error,返回错误根本原因

  1. 包装Error:fmt.Errorf 来包装 error,需要使用 %w 的格式化
    return fmt.Errorf("add error info: %+v, origin error: %w", "other info", err)
  2. 解包Error
    源码:
func Unwrap(err error) error {  
   //断言转化 err类型为一个实现了Unwarp()方法的interface对象
   u, ok := err.(interface {  
      Unwrap() error  
   }) 
   //断言失败 
   if !ok {  
      return nil  
   }
   //继续解包 
   return u.Unwrap()  
}
1.2.2.2、Is(err, target error) bool

Is(err, target error) bool :查看调用链是否与包含target错误
源码:

func Is(err, target error) bool {  
   if target == nil {  
      return err == target  
   }  
   //类型是否可比
   isComparable := reflectlite.TypeOf(target).Comparable()  
   for {  
	  //与目标 err相同
      if isComparable && err == target {  
         return true  
      }
      //递归继续向下查找错误链上是否包含指定目标错误  
      if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(target) {  
         return true  
      } 
      //解包到最后依然不存在,没有找到      
      if err = Unwrap(err); err == nil {  
         return false  
      }  
   }}
1.2.2.3、As(err error, target interface{}) bool

As(err error, target interface{}) bool : 剥离到给定target并将target设置到属性中去
源码:

func As(err error, target any) bool { 
   //校验转化目标是否为空
   //判空
   if target == nil {  
      panic("errors: target cannot be nil")  
   }   
   //拿到value
   val := reflectlite.ValueOf(target)
   //拿到value的实际类型reflect.type 
   typ := val.Type()
   //实际目标类型不是一个指针类型,或者是一个空指针类型  
   if typ.Kind() != reflectlite.Ptr || val.IsNil() {  
      panic("errors: target must be a non-nil pointer")  
   }
   //取出指针地址指向的对象   
   targetType := typ.Elem() 
   //判断其类是不是一个接口类型,以及是否实现了这个错误类型
   if targetType.Kind() != reflectlite.Interface && !targetType.Implements(errorType) {  
      panic("errors: *target must be interface or implement error")  
   }

   //校验待转化错误是否为空
   for err != nil {  
	  //判断是否可以将err分配到目标对象
      if reflectlite.TypeOf(err).AssignableTo(targetType) {  
		 //使用值进行设置到目标对象
         val.Elem().Set(reflectlite.ValueOf(err))  
         return true  
      }  
      //err实现了As(any) bool方法断言成功并且执行方法进行转化
      if x, ok := err.(interface{ As(any) bool }); ok && x.As(target) {  
         return true  
      }
	  //继续剥掉一层error看内部是否与给定匹配
      err = Unwrap(err)  
   }   
   return false  
}

二、异常处理

2.1、Panic

在Panic错误中一半使用recover函数进行恢复整个已经崩溃的程序进程,但是必须在defer中进行调用,才会生效。并且在函数调用的过程中可以在每个函数中都抛出panic逐层向外,直到不能捕获就会结束。

  • 有 panic 没 recover ,程序宕机。
  • 有 panic 有 recover ,程序正常。

使用方法:

func main(){
	panicTest()
}

func panicTest(){
	//do thomething

	//捕获Panic
	defer func (){
		if err:= recover(); err != nil{
			//deal error
		}
	}
}

2.2、Error

在GO语言中不存在传统意义上面try -catch语句,其设计思想如下:

  • 函数出现异常,就会将异常作为返回值,不存在异常的时候就会返回nil
  • 在外部应该使用if进行捕获异常进行处理
err,ans = ErrorDemo()
if err != nil{
	//处理异常
}

三、优雅异常处理

#todo

  • 优雅的异常处理

  1. defer_语句会将其后面跟随的语句进行延迟处理,在 defer 归属的函数即将返回时,将延迟处理的语句按 defer 的逆序进行执行(多个执行入栈,出栈) ↩︎

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

不会拯救世界的SuperMan

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

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

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

打赏作者

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

抵扣说明:

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

余额充值