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. 微服务网关从零搭建——(三)Ocelot网关 + identity4

    增加验证服务 1.创建名为AuthService 的core 空项目 2.修改startup文件 using System; using System.Collections.Generic; usi ...

  2. webpack之webpack-dev-server 与 webpack-hot-server

    最近在弄框架,用到了webpack打包,当然不可避免的遇到了开发实际问题.在实际开发中,我们不可能改一个文件,就去构建一次,于是想到了实时更新.查看webpack,看到了webpack-dev-ser ...

  3. buf.readUInt8()

    buf.readUInt8(offset[, noAssert]) offset {Number} 0 <= offset <= buf.length - 1 noAssert {Bool ...

  4. 使用Postman Interceptor发送带cookie的请求一直loading的解决法案

    很多web网页开发人员都知道Postman限制由于chrome安全的限制,发不出带cookie和带有自定义头部标签的请求.想要发出由于chrome安全的限制,发不出带cookie和带有自定义头部标签的 ...

  5. 微信小程序开发过程中tabbar页面显示的相关问题及解决办法!

    在微信小程序的开发过程中如果有使用过tabbar的同学,我相信一定会遇到一些困扰.为什么有些时候代码中明明已经在app.json里面增加了tabbar,可以页面中就是不显示呢?可不可以有些页面显示ta ...

  6. java 十四周总结

  7. python输出带颜色字体详解

    在python开发的过程中,经常会遇到需要打印各种信息.海量的信息堆砌在控制台中,就会导致信息都混在一起,降低了重要信息的可读性.这时候,如果能给重要的信息加上字体颜色,那么就会更加方便用户阅读了. ...

  8. Java基础学习总结(83)——Java泛型总结

    1. 什么是泛型? 泛型(Generic type 或者 generics)是对 Java 语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类.可以把类型参数看作是使用参数化类型时指定的类型 ...

  9. 使用Mybatis-Generator自动生成Dao、Model、Mapping相关文件(转)-----https://www.cnblogs.com/smileberry/p/4145872.html

    https://www.cnblogs.com/smileberry/p/4145872.html 使用Mybatis-Generator自动生成Dao.Model.Mapping相关文件(转)

  10. C#装饰模式

    using System;using System.Collections.Generic;using System.Text; namespace 装饰模式{    class Person    ...