目录

文章目录

  • 目录
  • Golang 的错误处理哲学
  • 为什么 Golang 不使用异常进行错误处理?
  • 错误处理
  • 错误处理策略
  • 建议

Golang 的错误处理哲学

首先需要注意的是,错误与异常有着本质的区别。

  • 错误(Error):作为流程的一部分,被调用方显式返回,调用方主动处理。
  • 异常(Exception):预料之外出现或者在流程中不应该出现的错误。

Golang 的特征是不使用异常进行错误处理,这一点有别于 Python、JavaScript 等变成语言。

Golang 的错误处理哲学就是:将错误视为编写函数的一等公民(返回值)。认为错误是正常流程的一部分,应该直接返回(return)。

例如:

func getUserFromDB() (*User, error) { ... }func main() {user, err := getUserFromDB()
}

在阅读代码时,开发者可以清晰的看到存在的错误。相较于其他语言,例如:Python,可能就无法这么清晰的来展现一个错误了,因为 try/catch 在处理控制流方面是完全不透明的。

以标准的方式处理 Golang 中的错误,将获得以下好处:

  1. 没有隐藏的控制流。
  2. 没有意外未捕获的异常日志。
  3. 可以完全控制代码中的错误,可以选择处理,返回和执行任何其他操作。

Golang 为程序员提供了对错误处理的完全控制权,但也要你承担全部的责任。所以 Golang 的错误处理一直存在着争议。

为什么 Golang 不使用异常进行错误处理?

Golang 之禅提到了两个重要的谚语:

  1. 简单很重要。
  2. 为失败计划而不是成功。

对所有返回 (value, error) 的函数使用简单的 if err!= nil 语句有助于确保首先考虑程序失败的情况。而无需费心处理复杂的嵌套 try/catch 块,它们可以适当地处理所有可能出现的异常。

对于基于异常的代码,你可能会意识到这样的一个场景:你会依赖 try/catch 的全局捕获,而不会深入挖掘实际的具体错误类型。这就导致了,代码有实际的异常,但你并没有正确处理它。也就是说,基于异常的代码鼓励开发者不检查错误,而是认为某些异常(如果发生)将在运行时自动处理。

但是退后一步说,基于异常的语言有一个好处:即使程序在运行时发生了未被处理的异常,仍可以通过堆栈来跟踪它,而不会中止程序。

错误处理

Golang 的错误(Error)是一个接口,不是某种特定类型。通过内置的 error interface 提供了非常简单的错误处理机制。func f() (value, error) 的语法不仅易于理解,而且在任何 Golang 项目中都可确保语法的一致性。

error interface 的定义:

type error interface {Error() string
}

在代码中,可以通过实现 error 接口类型来生成错误信息。函数通常在最后的返回值中返回错误信息。使用 errors.New 可返回一个自定义的错误信息:

func Sqrt(f float64) (float64, error) {if f < 0 {return 0, errors.New("math: square root of negative number")}// 实现
}result, err:= Sqrt(-1)if err != nil {fmt.Println(err)
}

if err != nil 模式的优势在于,通过错误链能方便遍历程序层次结构,直到发生错误的地方。例如,由程序的 main 函数处理的常见 Go 错误可能如下所示:

[2020-07-05-9:00] ERROR: Could not create user: could not check if user already exists in DB: could not establish database connection: no internet

上面的错误链是清晰的,对于应用程序的哪一层函数出错了有足够的信息。这样的错误输出有别于难以理解的异常堆栈跟踪,程序员可以添加友好的可读上下文描述这些错误的原因,并且通过清晰错误链进行处理。

这种错误链自然成为了标准 Golang 程序结构的一部分,可能看起来像这样:

// In controllers/user.go
if err := db.CreateUser(user); err != nil {return fmt.Errorf("could not create user: %w", err)
}// In database/user.go
func (db *Database) CreateUser(user *User) error {ok, err := db.DoesUserExist(user)if err != nil {return fmt.Errorf("could not check if user already exists in db: %w", err)}...
}func (db *Database) DoesUserExist(user *User) error {if err := db.Connected(); err != nil {return fmt.Errorf("could not establish db connection: %w", err)}...
}func (db *Database) Connected() error {if !hasInternetConnection() {return errors.New("no internet connection")}...
}

并且,如果你还希望将堆栈跟踪附加到函数中,可以使用 github.com/pkg/errors 库,它将打印出堆栈跟踪以及开发者构建的错误链。

errors.Wrapf(err, "could not save user with email %s", email)

示例:

package mainimport ("fmt"
)type divideError struct {dividee intdivider int
}func (de *divideError) Error() string {strFormat := `Cannot proceed, the divider is zero.dividee: %ddivider: 0
`return fmt.Sprintf(strFormat, de.dividee)
}func divide(varDividee int, varDivider int) (result int, errorMsg string) {if varDivider == 0 {dData := divideError{dividee: varDividee,divider: varDivider,}errorMsg = dData.Error()return 0, errorMsg}return varDividee / varDivider, ""
}func main() {if result, errorMsg := divide(100, 10); errorMsg == "" {fmt.Println("100/10 = ", result)}if _, errorMsg := divide(100, 0); errorMsg != "" {fmt.Println("errorMsg is: ", errorMsg)}}

错误处理策略

  1. 将错误返回给调用者。
resp, err := http.Get(url) if err != nil{return nill, err
}
  1. 构造新的信息返回给调用者。
doc, err := html.Parse(resp.Body)
resp.Body.Close()if err != nil {return nil, fmt.Errorf("parsing %s as HTML: %v", url,err)
}
  1. 如果错误发生后,程序无法继续运行,我们就可以采用第三种策略:输出错误信息并结束程序。需要注意的是,这种策略只应在 main() 函数中执行。对库函数而言,应仅向上传播错误,除非该错误意味着程序内部包含不一致性,即遇到了 bug,才能在库函数中结束程序。
if err := WaitForServer(url); err != nil { fmt.Fprintf(os.Stderr, "Site is down: %v\n", err) os.Exit(1)
}
  1. 有时,我们只需要输出错误信息就足够了,不需要中断程序的运行。我们可以通过 log 包提供函数,或者标准错误流输出错误信息。
if err := Ping(); err != nil {log.Printf("ping failed: %v; networking disabled",err)
}
// or
if err := Ping(); err != nil {fmt.Fprintf(os.Stderr, "ping failed: %v; networking disabled\n", err)
}
  1. 我们可以直接忽略掉错误。
dir, err := ioutil.TempDir("", "scratch")
if err != nil {return fmt.Errorf("failed to create temp dir: %v",err)
}// ...use temp dir...
os.RemoveAll(dir)

尽管 os.RemoveAll 会失败,但上面的例子并没有做错误处理。这是因为操作系统会定期的清理临时目录。正因如此,虽然程序没有处理错误,但程序的逻辑不会因此受到影响。

我们应该在每次函数调用后,都养成考虑错误处理的习惯,当你决定忽略某个错误时,你应该在清晰的记录下你的意图。

建议

  1. 当你的错误需要服务于开发人员时,建议添加堆栈跟踪。
  2. 对返回的错误进行处理,不要只是将它们忽略。
  3. 保持错误链的友好且明确。

Go 语言编程 — 错误处理相关推荐

  1. 怎样判断c语言编程错误,C语言编程出错

    已结贴√ 问题点数:20 回复次数:7 C语言编程出错 /***********选择工作模式***********/ void xuan()                               ...

  2. 最近的C语言编程错误小结

    1.can't find source file:通常是引用库函数时用了错误的参数 2.段错误:核心已转存 往往是文件指针出错,比如文件不存在或者没权限打开文件 3.大工程中独立接口编程时,有些已经被 ...

  3. 《C语言程序设计:问题与求解方法》——3.9节常见编程错误

    本节书摘来自华章社区<C语言程序设计:问题与求解方法>一书中的第3章,第3.9节常见编程错误,作者:何 勤,更多章节内容可以访问云栖社区"华章社区"公众号查看 3.9 ...

  4. c语言程序框一点数字就消失,你用C语言编程时,会犯下面的错误吗?

    原标题:你用C语言编程时,会犯下面的错误吗? C编译的程序对语法检查并不像其它高级语言那么严格,这就给编程人员留下"灵活的余地",但还是由于这个灵活给程序的调试带来了许多不便,尤其 ...

  5. c语言中错误executing,C语言编程中常见问题解读.doc

    C语言编程中常见问题解读 为什么vc6打开的文件却不能编译1 为什么一个vc6工程中有两个main函数不能编译成功?我想写两个程序怎么办?2 为什么v6不能编译程序,生成信息说compile和link ...

  6. c语言Max错误,C语言编程常见错误.pdf

    C 语言常见错误 版权声明:以下内容来自互联网 C 语言的最大特点是:功能强.使 方便灵活.C 编译的程序对语法检查并不象其它高级语言那么严 格,这就给编程人员留下 "灵活的余地" ...

  7. go语言特性理解--变量/函数/面向对象/并发编程/错误处理

    1. 变量 变量的声明很像 javascript,使用 var关键字.注意:go是静态类型的语言 //声明初始化一个变量 var x int = 100 var str string = " ...

  8. vdcode C语言不能弹出运行窗口_C语言编程常见问题分析,以及错误解决办法!

    遇到错误怎么办 在遇到错误的时候不要慌,首先看报错,中文直接看,英文翻译看.根据报错找到错误发生的行数,一般错误就在这一行的上下几行代码.不要偷懒遇到问题马上问别人是对自己的不负责,成长是需要经历困难 ...

  9. c语言错误出在花括号上,初学者在学习c语言编程过程中常出现的错误分析

    在高校经常中开设的第一科关于程序设计的语言就是C语言,这种语言有很多优势,比如说使用比较灵活,功能比较强大,可以用于编写程序.软件,但是对于初学者来说, 1 0 8 应用方法论 22第霸 0赫 6乱 ...

最新文章

  1. Go 语言编程 — go-swagger OpenAPI 工具
  2. linux的grup文件,Linux /boot/grub/grub.conf(GRUB配置文件)内容详解
  3. 如何判断LSTM模型中的过拟合和欠拟合 By 机器之心2017年10月02日 11:09 判断长短期记忆模型在序列预测问题上是否表现良好可能是一件困难的事。也许你会得到一个不错的模型技术得分,但了解
  4. python自动化从零开始_从零开始的自动化测试框架——Python篇
  5. Linq to xml:使用 XSLT 转换 XML 树
  6. 同时支持EF+Dapper的混合仓储,助你快速搭建数据访问层
  7. 中国癌症大数据出来了!每年126万例癌症死亡本可避免
  8. Jmeter七、jmeter中的参数化
  9. 【大数据编程笔记】大数据背景,案例,概念
  10. JS 将图片编码BASE64
  11. 由于没有公钥,无法验证下列签名
  12. hdb3编码规则波形_“10000101”所对应的HDB3编码波形是______。
  13. python批量实现百度网盘链接有效性检测
  14. 定风波·三月七日(苏轼)
  15. jQuery控制网页字体大小
  16. 实习面试感悟-阿里云
  17. 不做自了汉,大家好才是真的好
  18. 最全的PC【UA】UserAgent大全
  19. 虚拟化:gva、gpa、hva、hpa转化
  20. BLE中GATT理解

热门文章

  1. Kali Linux安全渗透教程(内部资料)
  2. python新闻系统_干货 | Python 实现新闻系统内容的增删改查功能
  3. 《机器学习实战》第5章 随机梯度上升算法
  4. 搜集了一些模型,精模低模都有
  5. 在腾讯做只有10万人用的产品,这群工程师竟然还很高兴
  6. 原来,苹果一直在给自家应用开防火墙绿灯!
  7. 西瓜说 | 物联网说了好一阵了,你为什么还不懂?
  8. 百度景鲲:AI交互正在吃掉旧产品边界,触达移动互联网盲区用户 | MEET2020
  9. 430亿晶体管,1020万逻辑单元,英特尔发布全球最大容量FPGA,用全新硬件加速AI开发...
  10. 行家来信 | 我在国际AI开源组织LFAI当董事的200天