Golang中错误处理

简介: Golang中错误处理

Golang中错误处理

前言

在起初golang基础并未打扎实直接去学后端的gin框架,但在之前并未学习
go的错误处理,在学习gin时发现在写代码的时候几乎有将近一半的代码都在
进行错误处理日志收集。的确这样做下来在debug的时候会比较的轻松。但是我一点没有系统的看过go中的errors里的方法,只是跟着gin里的项目学
习了zap日志库与errors的一些方法配合使用。今天就看一下Go核心编程里的错误处理。

一、基本概念与使用

error类型其实是一个接口类型,也是一个 Go 语言的内建类型。在这个接口类型的声明中只包含了一个方法Error。Error方法不接受任何参数,但是会返回一个string类型的结果。它的作用是返回错误信息的字符串表示形式。我们使用error类型的方式通常是,在函数声明的结果列表的最后,声明一个该类型的结果,同时在调用这个函数之后,先判断它返回的最后一个结果值是否“不为nil”。
比如代码中所写:


package main

import (
  "errors"
  "fmt"
)

func echo(request string) (response string, err error) {
   
  if request == "" {
   
    err = errors.New("empty request")
    return
  }
  response = fmt.Sprintf("echo: %s", request)
  return
}

func main() {
   
  for _, req := range []string{
   "", "hello!"} {
   
    fmt.Printf("request: %s\n", req)
    resp, err := echo(req)
    if err != nil {
   
      fmt.Printf("error: %s\n", err)
      continue
    }
    fmt.Printf("response: %s\n", resp)
  }
}

代码中两个值得注意的地方
第一,在echo函数和main函数中,都使用到了卫述语句。简单地讲,它就是被用来检查后续操作的前置条件并进行相应处理的语句。也就是我在前言提到的在写代码时铺天盖地的语句。


第二对于echo函数来说,它进行常规操作的前提是:传入的参数值一定要符合要求。而对于调用echo函数的程序来说,进行后续操作的前提就是echo函数的执行不能出错。

二、对于具体错误的判断,Go 语言中都有哪些惯用法?

对于类型在已知范围内的一系列错误值,一般使用类型断言表达式或类型switch语句来判断;


对于已有相应变量且类型相同的一系列错误值,一般直接使用判等操作来判断;


对于没有相应变量且类型未知的一系列错误值,只能使用其错误信息的字符串表示形式来做判断。而这种是我常用的一种方法。


对于第一种情况,如果我们已知错误类型
就有以下案例:


func underlyingError(err error) error {
   
  switch err := err.(type) {
   
  case *os.PathError:
    return err.Err
  case *os.LinkError:
    return err.Err
  case *os.SyscallError:
    return err.Err
  case *exec.Error:
    return err.Err
  }
  return err
}

函数underlyingError的作用是:
获取和返回已知的操作系统相关错误的潜在错误值。其中的类型switch语句中有若干个case子句,分别对应了上述几个错误类型。当它们被选中时,都会把函数参数err的Err字段作为结果值返回。如果它们都未被选中,那么该函数就会直接把参数值作为结果返回,即放弃获取潜在错误值。

三、怎样根据实际情况给予恰当的错误值?

构建错误值体系的基本方式有两种,即:
创建立体的错误类型体系创建扁平的错误值列表


对于立体的错误类型体系。由于在 Go 语言中实现接口是非侵入式的,所以我们可以做得很灵活。

例如在标准库的net代码包中,有一个名为Error的接口类型。它算是内建接口类型error的一个扩展接口,因为error是net.Error的嵌入接口

立体的错误体系可以把一个包的错误类型想象成一棵树,内建接口error就是树的根,而net.Error接口就是一个在根上延伸的第一级非叶子节点。


首先判断它是否是net.Error类型的,也就是说该值是否代表了一个网络相关的错误。如果是,那么我们还可以再进一步判断它的类型是哪一个更具体的错误类型,这样就能知道这个网络相关的错误具体是由于操作不当引起的,还是因为网络地址问题引起的,又或是由于网络协议不正确引起的。
这样就可以模拟出立体的一个体系一层一层的去判断。

用类型建立起树形结构的错误体系,用统一字段建立起可追根溯源的链式错误关联。


对于扁平的错误值列表
当我们只是想预先创建一些代表已知错误的错误值时候,扁平的
错误值列表无疑是最好的选择。

但是问题就来了
由于error是接口类型,所以通过errors.New函数生成的错误值只能被赋给变量,而不能赋给常量,又由于这些代表错误的变量需要给包外代码使用,所以其访问权限只能是公开的。

如果说恶意代码改变了这些公开变量的值,那么程序的功能就必然会受到影响。因为在这种情况下我们往往会通过判等操作来判断拿到的错误值具体是哪一个错误,如果这些公开变量的值被改变了,那么相应的判等操作的结果也会随之改变。

当然对此也有解决的方案:
先私有化此类变量,也就是说,让它们的名称首字母变成小写,然后编写公开的用于获取错误值以及用于判等错误值的函数。
比如,对于错误值os.ErrClosed,先改写它的名称,让其变成os.errClosed,然后再编写ErrClosed函数和IsErrClosed函数。

相关文章
|
3月前
|
Go
Golang语言错误处理机制
这篇文章是关于Golang语言错误处理机制的教程,介绍了使用defer结合recover捕获错误、基于errors.New自定义错误以及使用panic抛出自定义错误的方法。
55 3
|
5月前
|
中间件 测试技术 Go
Golang中的错误处理最佳实践
【7月更文挑战第10天】在Golang中,错误处理是核心且重要的。最佳实践包括:定义明确的错误类型,使用错误链(如`%w`包装错误),始终检查错误(避免忽略),谨慎使用`panic`和`recover`,利用多值返回处理错误,标准化错误处理逻辑,并确保测试错误处理代码。这些做法有助于构建健壮和可维护的程序。
|
存储 Java Go
100天精通Golang(基础入门篇)——第23天:错误处理的艺术: Go语言实战指南
100天精通Golang(基础入门篇)——第23天:错误处理的艺术: Go语言实战指南
68 0
|
存储 搜索推荐 网络协议
人非圣贤孰能无过,Go lang1.18入门精炼教程,由白丁入鸿儒,Go lang错误处理机制EP11
人非圣贤,孰能无过,有则改之,无则加勉。在编程语言层面,错误处理方式大体上有两大流派,分别是以Python为代表的异常捕获机制(try....catch);以及以Go lang为代表的错误返回机制(return error),前者是自动化流程,模式化的语法隔离正常逻辑和错误逻辑,而后者,需要将错误处理判断编排在正常逻辑中。虽然模式化语法更容易让人理解,但从系统资源开销角度看,错误返回机制明显更具优势。
人非圣贤孰能无过,Go lang1.18入门精炼教程,由白丁入鸿儒,Go lang错误处理机制EP11
Golang:error包错误处理
Golang:error包错误处理
105 0
|
3月前
|
Go
Golang语言之管道channel快速入门篇
这篇文章是关于Go语言中管道(channel)的快速入门教程,涵盖了管道的基本使用、有缓冲和无缓冲管道的区别、管道的关闭、遍历、协程和管道的协同工作、单向通道的使用以及select多路复用的详细案例和解释。
142 4
Golang语言之管道channel快速入门篇
|
3月前
|
Go
Golang语言文件操作快速入门篇
这篇文章是关于Go语言文件操作快速入门的教程,涵盖了文件的读取、写入、复制操作以及使用标准库中的ioutil、bufio、os等包进行文件操作的详细案例。
72 4
Golang语言文件操作快速入门篇
|
3月前
|
Go
Golang语言之gRPC程序设计示例
这篇文章是关于Golang语言使用gRPC进行程序设计的详细教程,涵盖了RPC协议的介绍、gRPC环境的搭建、Protocol Buffers的使用、gRPC服务的编写和通信示例。
117 3
Golang语言之gRPC程序设计示例
|
3月前
|
安全 Go
Golang语言goroutine协程并发安全及锁机制
这篇文章是关于Go语言中多协程操作同一数据问题、互斥锁Mutex和读写互斥锁RWMutex的详细介绍及使用案例,涵盖了如何使用这些同步原语来解决并发访问共享资源时的数据安全问题。
100 4