5.2 测试多返回值函数的错误

Go 语言的函数经常使用两个返回值来表示执行是否成功:返回某个值以及 true 表示成功;返回零值(或 nil)和 false 表示失败(第 4.4 节)。当不使用 true 或 false 的时候,也可以使用一个 error 类型的变量来代替作为第二个返回值:成功执行的话,error 的值为 nil,否则就会包含相应的错误信息(Go 语言中的错误类型为 error: var err error,我们将会在第 13 章进行更多地讨论)。这样一来,就很明显需要用一个 if 语句来测试执行结果;由于其符号的原因,这样的形式又称之为 comma,ok 模式(pattern)。

在第 4.7 节的程序 string_conversion.go 中,函数 strconv.Atoi 的作用是将一个字符串转换为一个整数。之前我们忽略了相关的错误检查:

anInt, _ = strconv.Atoi(origStr)

如果 origStr 不能被转换为整数,anInt 的值会变成 0 而 _ 无视了错误,程序会继续运行。

这样做是非常不好的:程序应该在最接近的位置检查所有相关的错误,至少需要暗示用户有错误发生并对函数进行返回,甚至中断程序。

我们在第二个版本中对代码进行了改进:

示例 1:

示例 5.3 string_conversion2.go

package main

import (
"fmt"
"strconv"
) func main() {
var orig string = "ABC"
// var an int
var newS string
// var err error fmt.Printf("The size of ints is: %d\n", strconv.IntSize)
// anInt, err = strconv.Atoi(origStr)
an, err := strconv.Atoi(orig)
if err != nil {
fmt.Printf("orig %s is not an integer - exiting with error\n", orig)
return
}
fmt.Printf("The integer is %d\n", an)
an = an + 5
newS = strconv.Itoa(an)
fmt.Printf("The new string is: %s\n", newS)
}

这是测试 err 变量是否包含一个真正的错误(if err != nil)的习惯用法。如果确实存在错误,则会打印相应的错误信息然后通过 return 提前结束函数的执行。我们还可以使用携带返回值的 return 形式,例如 return err。这样一来,函数的调用者就可以检查函数执行过程中是否存在错误了。

习惯用法

value, err := pack1.Function1(param1)
if err != nil {
fmt.Printf("An error occured in pack1.Function1 with parameter %v", param1)
return err
}
// 未发生错误,继续执行:

由于本例的函数调用者属于 main 函数,所以程序会直接停止运行。

如果我们想要在错误发生的同时终止程序的运行,我们可以使用 os 包的 Exit 函数:

习惯用法

if err != nil {
fmt.Printf("Program stopping with error %v", err)
os.Exit(1)
}

(此处的退出代码 1 可以使用外部脚本获取到)

有时候,你会发现这种习惯用法被连续重复地使用在某段代码中。

当没有错误发生时,代码继续运行就是唯一要做的事情,所以 if 语句块后面不需要使用 else 分支。

示例 2:我们尝试通过 os.Open 方法打开一个名为 name 的只读文件:

f, err := os.Open(name)
if err != nil {
return err
}
doSomething(f) // 当没有错误发生时,文件对象被传入到某个函数中
doSomething

练习 5.1 尝试改写 string_conversion2.go 中的代码,要求使用 := 方法来对 err 进行赋值,哪些地方可以被修改?

示例 3:可以将错误的获取放置在 if 语句的初始化部分:

习惯用法

if err := file.Chmod(0664); err != nil {
fmt.Println(err)
return err
}

示例 4:或者将 ok-pattern 的获取放置在 if 语句的初始化部分,然后进行判断:

习惯用法

if value, ok := readData(); ok {

}

注意事项

如果您像下面一样,没有为多返回值的函数准备足够的变量来存放结果:

func mySqrt(f float64) (v float64, ok bool) {
if f < 0 { return } // error case
return math.Sqrt(f),true
} func main() {
t := mySqrt(25.0)
fmt.Println(t)
}

您会得到一个编译错误:multiple-value mySqrt() in single-value context

正确的做法是:

t, ok := mySqrt(25.0)
if ok { fmt.Println(t) }

注意事项 2

当您将字符串转换为整数时,且确定转换一定能够成功时,可以将 Atoi 函数进行一层忽略错误的封装:

func atoi (s string) (n int) {
n, _ = strconv.Atoi(s)
return
}

实际上,fmt 包(第 4.4.3 节)最简单的打印函数也有 2 个返回值:

count, err := fmt.Println(x) // number of bytes printed, nil or 0, error

当打印到控制台时,可以将该函数返回的错误忽略;但当输出到文件流、网络流等具有不确定因素的输出对象时,应该始终检查是否有错误发生(另见练习 6.1b)。

摘自:https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/05.2.md

go语言笔记——多值函数,本质上和nodejs的回调很像,不过nodejs是回调的第一个参数是err,而golang里是第二个!的更多相关文章

  1. Facebook图片存储系统Haystack——存小文件,本质上是将多个小文件合并为一个大文件来降低io次数,meta data里存偏移量

    转自:http://yanyiwu.com/work/2015/01/04/Haystack.html 一篇14页的论文Facebook-Haystack, 看完之后我的印象里就四句话: 因为[传统文 ...

  2. Go语言笔记[实现一个Web框架实战]——EzWeb框架(一)

    Go语言笔记[实现一个Web框架实战]--EzWeb框架(一) 一.Golang中的net/http标准库如何处理一个请求 func main() { http.HandleFunc("/& ...

  3. javascript 核心语言笔记 6 - 对象

    对象是 JavaScript 的基本数据类型.是一种复合值:将很多值聚合在一起.对象可以看做是无序集合,每个属性都是一个名/值对.这种基本数据结构还有很多叫法,比如「散列」(hash).「散列表」(h ...

  4. 011-指针(上)-C语言笔记

    011-指针(上)-C语言笔记 学习目标 1.[掌握]字符串常用函数 2.[掌握]指针变量的声明 3.[掌握]指针变量的初始化 4.[掌握]函数与指针 5.[掌握]指针的数据类型 6.[掌握]多级指针 ...

  5. 005-循环结构(上)-C语言笔记

    005-循环结构(上)-C语言笔记 学习目标 1.[掌握]switch-case结构 2.[理解]case语句穿透 3.[理解]Xcode断点调试 4.[理解]while循环结构初体验 5.[掌握]w ...

  6. C++学习笔记----3.2 C++引用在本质上是什么,它和指针到底有什么区别

    从概念上讲.指针从本质上讲就是存放变量地址的一个变量,在逻辑上是独立的,它可以被改变,包括其所指向的地址的改变和其指向的地址中所存放的数据的改变. 而引用是一个别名,它在逻辑上不是独立的,它的存在具有 ...

  7. 瘋耔java语言笔记

    一◐ java概述                                                                                        1.1 ...

  8. R语言笔记

    R语言笔记 学习R语言对我来说有好几个地方需要注意的,我觉得这样的经验也适用于学习其他的新的语言. 语言的目标 我理解语言的目标就是这个语言是用来做什么的,为什么样的任务服务的,也就是设计这个语言的动 ...

  9. Scala语言笔记 - 第一篇

    目录 Scala语言笔记 - 第一篇 1 基本类型和循环的使用 2 String相关 3 模式匹配相关 4 class相关 5 函数调用相关 Scala语言笔记 - 第一篇 ​ 最近研究了下scala ...

随机推荐

  1. MySQLWorkBench怎么设置主键自增长

    参考 https://blog.csdn.net/qq_40472613/article/details/87858099 勾选AI选项,相当于执行了这个语句: AUTO_INCREMENT表示自增 ...

  2. Django之auth登录认证

    前言:我们在开发一个网站的时候,无可避免的需要设计实现网站的用户系统.此时我们需要实现包括用户注册.用户登录.用户认证.注销.修改密码等功能,这还真是个麻烦的事情呢. Django作为一个完美主义者的 ...

  3. <东方梦符祭> N2无尽40波通关

    先上图吧 阵容:纯粹 + 伪魔法队 主C:神妈 露米娅(我觉得不厉害了) 灵梦 控制:琪露诺 + 蕾蒂 永江依玖(听说很厉害 没培育满 没看到效果) 挂件:铃仙挂机 帕秋莉 大妖精(链神妈) 圣今天才 ...

  4. 文艺平衡树-splay的区间操作

    真的是个神题,蒟蒻表示无力吐槽.刚开始以为是一个板子题,看着题解打了一遍,大概也理解了他是怎么实现的,然后我就去做别的题了,然后就在Three_D大佬的询问下蒙*了.最后还是问的nc哥,并思考了一个中 ...

  5. 简单了解jdbcTemplate的queryForList是如何查询

    queryForList方法会调用query方法,同时会传入一个新的ColumnMapRowMapper对象 ArgumentPreparedStatementSetter对象只有一个Object[] ...

  6. 每日命令:(3)pwd

    Linux中用 pwd 命令来查看”当前工作目录“的完整路径. 简单得说,每当你在终端进行操作时,你都会有一个当前工作目录. 在不太确定当前位置时,就会使用pwd来判定当前目录在文件系统内的确切位置. ...

  7. centos7修改时间和时区

    设置时区同样, 在 CentOS 7 中, 引入了一个叫 timedatectl 的设置设置程序. 用法很简单: # timedatectl # 查看系统时间方面的各种状态 Local time: 四 ...

  8. react-router v4 学习实践

    最近学习了 react-router v4,根据官方 API 文档和网上资源做了一个简单的路由示例. 先用官方的工具 create-react-app  初始化一个 react 项目模板,再根据自己的 ...

  9. python文件读写及形式转化和CGI的简单应用

    一丶python文件读写学习笔记 open() 将会返回一个 file 对象,基本语法格式如下: open(filename, mode) filename:包含了你要访问的文件名称的字符串值. mo ...

  10. 洛谷 1182 数列分段Section II

    [题解] 最大值最小化,那么一般要联想到二分.二分一个最大值,然后check一下能否分成小于等于m段即可. #include<cstdio> #include<algorithm&g ...