Golang语言异常机制解析:错误策略与优雅处理

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
云解析DNS-重点域名监控,免费拨测 20万次(价值200元)
简介: Golang语言异常机制解析:错误策略与优雅处理

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站前言


前言

作为开发者来说,我们没办法保证程序在运行过程中永远不会出现异常,对于异常,在很多编程语言中,可以用 try-catch语句来捕获,而Go语言的开发者显然觉得 try-catch被滥用了,因此 Go不支持使用 try-catch语句捕获异常处理。


那么,Go语言是如何定义和处理程序的异常呢?


Go语言的将程序运行出现的问题分为两种:错误(error)和异常(exception),错误是程序(比如一个函数)运行的预期结果之一,当你调用一个函数时,就应该处理出现错误的情况,而异常是不可预期的(当然也可以手动触发)且会导致程序中断运行的严重错误。

Go错误处理策略

编写Go语言程序,一般推荐通过函数的最后一个返回值告诉调用者函数是否执行成功,可以分两种情况来讨论:

第一种情况,如果函数的执行结果只有正确与失败,那么返回值可以是 boolean类型:

func exists(key string) bool {
  //to check if the key is exists
  return true
}
 
if ok := exists("test"); ok {
  // do something
}


第二种情况,如果要让调用者得到更详细的错误信息,显然只返回一个布尔值是不够的,这时候可以返回一个 error类型,error类型是一个接口,只有一个 Error方法的接口:

type error interface {
  Error() string
}

通过 error类型的 Error方法,可以获得更详细的错误信息,方便调用者做进一步的处理,因此在 Go标准库的方法和函数中,一般都会将 error类型作为最后一个返回值,比如我们以前文章中讲到的 os包下的 OpenCreate函数:

package os
 
func Open(name string) (*File, error) {
  return OpenFile(name, O_RDONLY, 0)
}
 
func Create(name string) (*File, error) {
  return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)
}

当函数最后一个返回值 error不为 nil时,表示执行不成功,此时其他的返回值就应该忽略:

file,err := os.Open("./my.dat")
 
if err != nil{
    return err
}
 
defer file.Close()

上面代码中,如果 error不为 nil,那么变量 file则为 nil,表示无法获得一个文件句柄,这时候直接返回错误,因为如果再继续往下执行,可能会引发程序崩溃。


当然并不是所有情况下一遇到返回的 error不为 nil,就要抛弃其他返回值,比如使用 Read()方法读取文件时:

Read(p []byte) (n int, err error)


在读取文件到结尾时 Read方法会返回一个 io.EOF的错误:

var EOF = errors.New("EOF")

此时虽然 error不为 nil,但仍然应该把读取到的字节数组保存,而不是抛弃掉。

创建error的几种方式

当我们开发自己的函数时,也可以创建自己的错误类型,有以下几种方式:


errors包

errors包下的 New()函数可以创建一个只有文本信息的 error类型:

package main
 
func main() {
  var s = []int{1, 2, 3}
  s[3] = 10
}


fmt包

fmt包下的 Errorf函数可以将文本格式后作为error类型的错误信息,并返回一个error类型,因此其作用与errors.New函数类似

func getFile(name string)(*os.file,error){
  if name == ""{
    return nil,fmt.Errorf("file name could not be empty")
  }
}

fmt.Errorf函数还能封装其他 error类型,再返回一个新的 error类型,以此形成一条完整的错误链条:

doc, err := html.Parse(resp.Body)
resp.Body.Close()
if err != nil {
  return nil, fmt.Errorf("parsing %s as HTML: %v", url,err)
}
自定义错误类型

其实,上面两种创建错误类型的方式,本质上都是实现 error接口,我们也可以创建一个拥有 Error方法的类型来实现 error接口:

type Result struct {
  Code    int
  Message string
  Data    interface{}
}
 
func (r Result) Error() string {
  return r.Message
}

如何处理错误

当调用的函数或方法的返回值有 error类型时,最简单的当然可以选择直接忽略错误,不过更恰当的方式是处理对应的错误,有以下几种处理策略:


直接返回错误

对于函数来说,如果在执行时遇到错误,可以直接返回给上层调用者:

func SendMessage(url string) error {
  if url == ""{
    return errors.New("url can't not be empty")
  }
  _, err := http.Get(url)
  if err != nil {
    return err
  }
  return nil
}
记录日志并继续运行

当调用函数时遇到返回的错误,如果不影响程序运行,也可以选择记录错误日志并往下执行:

if err := SendMessage("https://xxx/sendMessage");err != nil{
  log.Printf("the message sending been broken by : %v\n", err)
}
 
记录日志并结束运行

如果错误影响程序的执行,也可以记录日志后,退出程序执行:

if err := SendMessage("https://xxx/sendMessage");err != nil{
  log.Printf("the message sending been broken by : %v\n", err)
  os.Exit(1)
}

记录日志并退出执行,直接用 log包的 Fatalf函数就可以做到,因此上面代码的更简洁做法是:

if err := SendMessage("https://xxx/sendMessage");err != nil{
  log.Fatalf("the message sending been broken by : %v\n", err)
}


Go异常处理机制

在Go语言中,异常是指会引发程序崩溃无法继续运行的错误,比如数组越界或者空指针引用等情况,这时候会触发 panic异常:

package main
 
func main() {
  var s = []int{1, 2, 3}
  s[3] = 10
}


上面程序运行结果如下,可以看出触发了数组越界的异常:

panic: runtime error: index out of range [3] with length 3


无论是在主协程还是子协程中,一旦触发 panic异常,整个程序都会终止运行。

panic函数

除了数组越界等不可预测的异常会自动触发 panic,也可以手动调用 panic函数触发异常来终止程序的运行:

func loadConfig(path string){
  panic("can't load the config file on path " + path)
}


一个良好的程序最好不要主动调用 panic函数,尤其是开发类库的时候,最好通过返回 error类型来告诉调用者发生了什么错误,而不是触发 panic导致程序终止运行。

recover函数

当发生 panic异常时,如果不捕获得异常,那么程序就是终止运行,在Go语言中,可以用 defer语句和 recover函数的模式来捕获 panic异常:

1package main
 
import (
  "fmt"
  "log"
)
 
func main() {
 
  n1 := FindElementByIndex(1)
  fmt.Println(n1)
  
  n2 := FindElementByIndex(4)
  fmt.Println(n2)
}
 
func FindElementByIndex(index int) int {
  defer func() {
    if e := recover(); e != nil {
      log.Fatal(e)
    }
  }()
  s := []int{1, 2, 3, 4}
  return s[index]
}
 


总结

   本文深入探讨了Go语言的错误策略与异常机制。主要介绍了错误处理的重要性,以及Go语言中的错误类型和处理函数。此外还讨论了Go语言的异常机制,包括panic和recover函数的使用。通过合理的错误处理和异常处理,我们可以提高代码的可维护性和可靠性,减少潜在的bug和故障。希望本文对您有帮助,感谢阅读~

相关文章
|
3月前
|
Linux Go iOS开发
IDA 9.2 发布:Golang 改进、新 UI 组件、类型解析等
IDA Pro 9.2 (macOS, Linux, Windows) - 强大的反汇编程序、反编译器和多功能调试器
705 0
|
10月前
|
机器学习/深度学习 传感器 监控
机器学习:强化学习中的探索策略全解析
在机器学习的广阔领域中,强化学习(Reinforcement Learning, RL)无疑是一个充满魅力的子领域。它通过智能体与环境的交互,学习如何在特定的任务中做出最优决策。然而,在这个过程中,探索(exploration)和利用(exploitation)的平衡成为了智能体成功的关键。本文将深入探讨强化学习中的探索策略,包括其重要性、常用方法以及代码示例来论证这些策略的效果。
|
9月前
|
前端开发 JavaScript Java
优雅草卓伊凡:全栈工程师并非盲目学语言,转型价值全解析
优雅草卓伊凡:全栈工程师并非盲目学语言,转型价值全解析
137 0
优雅草卓伊凡:全栈工程师并非盲目学语言,转型价值全解析
|
10月前
|
存储 监控 算法
探秘员工泄密行为防线:基于Go语言的布隆过滤器算法解析
在信息爆炸时代,员工泄密行为对企业构成重大威胁。本文聚焦布隆过滤器(Bloom Filter)这一高效数据结构,结合Go语言实现算法,帮助企业识别和预防泄密风险。通过构建正常操作“指纹库”,实时监测员工操作,快速筛查可疑行为。示例代码展示了如何利用布隆过滤器检测异常操作,并提出优化建议,如调整参数、结合日志分析系统等,全方位筑牢企业信息安全防线,守护核心竞争力。
|
10月前
|
存储 算法 安全
基于 Go 语言的公司内网管理软件哈希表算法深度解析与研究
在数字化办公中,公司内网管理软件通过哈希表算法保障信息安全与高效管理。哈希表基于键值对存储和查找,如用户登录验证、设备信息管理和文件权限控制等场景,Go语言实现的哈希表能快速验证用户信息,提升管理效率,确保网络稳定运行。
181 0
|
缓存 并行计算 Linux
深入解析Linux操作系统的内核优化策略
本文旨在探讨Linux操作系统内核的优化策略,包括内核参数调整、内存管理、CPU调度以及文件系统性能提升等方面。通过对这些关键领域的分析,我们可以理解如何有效地提高Linux系统的性能和稳定性,从而为用户提供更加流畅和高效的计算体验。
508 24
|
负载均衡 网络协议 定位技术
在数字化时代,利用DNS实现地理位置路由成为提升用户体验的有效策略
在数字化时代,利用DNS实现地理位置路由成为提升用户体验的有效策略。通过解析用户请求的来源IP地址,DNS服务器可判断其地理位置,并返回最近或最合适的服务器IP,从而优化网络路由,减少延迟,提高访问速度。示例代码展示了如何基于IP地址判断地理位置并分配相应服务器IP,实际应用中需结合专业地理数据库和动态调整机制,以应对复杂网络环境带来的挑战。
316 6
|
存储 监控 算法
Java虚拟机(JVM)垃圾回收机制深度解析与优化策略####
本文旨在深入探讨Java虚拟机(JVM)的垃圾回收机制,揭示其工作原理、常见算法及参数调优方法。通过剖析垃圾回收的生命周期、内存区域划分以及GC日志分析,为开发者提供一套实用的JVM垃圾回收优化指南,助力提升Java应用的性能与稳定性。 ####
|
机器学习/深度学习 存储 人工智能
AI助力电子邮件安全防护,CISO解析新策略
AI助力电子邮件安全防护,CISO解析新策略
|
存储 Go PHP
Go语言中的加解密利器:go-crypto库全解析
在软件开发中,数据安全和隐私保护至关重要。`go-crypto` 是一个专为 Golang 设计的加密解密工具库,支持 AES 和 RSA 等加密算法,帮助开发者轻松实现数据的加密和解密,保障数据传输和存储的安全性。本文将详细介绍 `go-crypto` 的安装、特性及应用实例。
618 0

推荐镜像

更多