tnblog
首页
视频
资源
登录
生活如此美好,你却如此暴躁...
排名
6
文章
6
粉丝
16
评论
8
{{item.articleTitle}}
{{item.blogName}} : {{item.content}}
ICP备案 :渝ICP备18016597号-1
网站信息:2018-2024TNBLOG.NET
技术交流:群号656732739
联系我们:contact@tnblog.net
欢迎加群交流技术

​5.1 测试多返回值函数的错误

3500人阅读 2021/4/10 19:08 总访问:18759 评论:1 收藏:0 手机
分类: golang
5.1 测试多返回值函数的错误


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


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


```go
anInt_ = strconv.Atoi(origStr)
```


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


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


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


示例 :
```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)
    anerr := 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`。这样一来,函数的调用者就可以检查
函数执行过程中是否存在错误了。


**习惯用法**


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


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


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


**习惯用法**


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


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


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


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


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


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


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


**习惯用法**


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


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


**习惯用法**


```go
if valueok := readData(); ok {
}
```


**注意事项**


如果您像下面一样,没有为多返回值的函数准备足够的变量来存放结果:
    
```go
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`。


正确的做法是:


```go
tok := mySqrt(25.0)
if ok { fmt.Println(t) }
```


**注意事项 2**


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


```go
func atoi (s string) (n int) {
    n_ = strconv.Atoi(s)
    return
}
```


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


```go
counterr := fmt.Println(x) // number of bytes printed, nil or 0, error
```


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


评价