Co-authored-by: Joe Chen <jc@unknwon.io>
This commit is contained in:
Haigang Zhou
2022-05-09 21:42:14 +08:00
committed by GitHub
parent 82026084b3
commit f5dae8f559
16 changed files with 273 additions and 267 deletions

View File

@@ -1,14 +1,14 @@
# 5.2 测试多返回值函数的错误
Go 语言的函数经常使用两个返回值来表示执行是否成功:返回某个值以及 true 表示成功;返回零值(或 nil和 false 表示失败(第 4.4 节)。当不使用 true 或 false 的时候,也可以使用一个 error 类型的变量来代替作为第二个返回值成功执行的话error 的值为 nil否则就会包含相应的错误信息Go 语言中的错误类型为 error: `var err error`,我们将会在第 13 章进行更多地讨论)。这样一来,就很明显需要用一个 if 语句来测试执行结果;由于其符号的原因,这样的形式又称之为 comma,ok 模式(pattern
Go 语言的函数经常使用两个返回值来表示执行是否成功:返回某个值以及 `true` 表示成功;返回零值(或 `nil`)和 `false` 表示失败([第 4.4 节](04.4.md))。当不使用 `true``false` 的时候,也可以使用一个 `error` 类型的变量来代替作为第二个返回值:成功执行的话,`error` 的值为 `nil`否则就会包含相应的错误信息Go 语言中的错误类型为 `error`: `var err error`,我们将会在[第 13 章](13.0.md) 进行更多地讨论)。这样一来,就很明显需要用一个 `if` 语句来测试执行结果;由于其符号的原因,这样的形式又称之为“逗号 ok 模式”(comma, ok pattern)
在第 4.7 节的程序 `string_conversion.go` 中,函数 `strconv.Atoi` 的作用是将一个字符串转换为一个整数。之前我们忽略了相关的错误检查:
[第 4.7 节](04.7.md) 的程序 [string_conversion.go](examples/chapter_4/string_conversion.go) 中,函数 `strconv.Atoi()` 的作用是将一个字符串转换为一个整数。之前我们忽略了相关的错误检查:
```go
anInt, _ = strconv.Atoi(origStr)
```
如果 origStr 不能被转换为整数anInt 的值会变成 0`_` 无视了错误,程序会继续运行。
如果 `origStr` 不能被转换为整数,`anInt` 的值会变成 `0``_` 无视了错误,程序会继续运行。
这样做是非常不好的:程序应该在最接近的位置检查所有相关的错误,至少需要暗示用户有错误发生并对函数进行返回,甚至中断程序。
@@ -47,7 +47,7 @@ func main() {
}
```
这是测试 err 变量是否包含一个真正的错误(`if err != nil`)的习惯用法。如果确实存在错误,则会打印相应的错误信息然后通过 return 提前结束函数的执行。我们还可以使用携带返回值的 return 形式,例如 `return err`。这样一来,函数的调用者就可以检查函数执行过程中是否存在错误了。
这是测试 `err` 变量是否包含一个真正的错误(`if err != nil`)的习惯用法。如果确实存在错误,则会打印相应的错误信息然后通过 `return` 提前结束函数的执行。我们还可以使用携带返回值的 `return` 形式,例如 `return err`。这样一来,函数的调用者就可以检查函数执行过程中是否存在错误了。
**习惯用法**
@@ -60,7 +60,7 @@ if err != nil {
// 未发生错误,继续执行:
```
由于本例的函数调用者属于 main 函数,所以程序会直接停止运行。
由于本例的函数调用者属于 `main` 函数,所以程序会直接停止运行。
如果我们想要在错误发生的同时终止程序的运行,我们可以使用 `os` 包的 `Exit` 函数:
@@ -73,11 +73,11 @@ if err != nil {
}
```
(此处的退出代码 1 可以使用外部脚本获取到)
(此处的退出代码 `1` 可以使用外部脚本获取到)
有时候,你会发现这种习惯用法被连续重复地使用在某段代码中。
当没有错误发生时,代码继续运行就是唯一要做的事情,所以 if 语句块后面不需要使用 else 分支。
当没有错误发生时,代码继续运行就是唯一要做的事情,所以 `if` 语句块后面不需要使用 `else` 分支。
示例 2我们尝试通过 `os.Open` 方法打开一个名为 `name` 的只读文件:
@@ -90,9 +90,9 @@ doSomething(f) // 当没有错误发生时,文件对象被传入到某个函
doSomething
```
**练习 5.1** 尝试改写 [string_conversion2.go](examples/chapter_5/string_conversion2.go) 中的代码,要求使用 `:=` 方法来对 err 进行赋值,哪些地方可以被修改?
**练习 5.1** 尝试改写 [string_conversion2.go](examples/chapter_5/string_conversion2.go) 中的代码,要求使用 `:=` 方法来对 `err` 进行赋值,哪些地方可以被修改?
示例 3可以将错误的获取放置在 if 语句的初始化部分:
示例 3可以将错误的获取放置在 `if` 语句的初始化部分:
**习惯用法**
@@ -103,7 +103,7 @@ if err := file.Chmod(0664); err != nil {
}
```
示例 4或者将 ok-pattern 的获取放置在 if 语句的初始化部分,然后进行判断:
示例 4或者将 ok-pattern 的获取放置在 `if` 语句的初始化部分,然后进行判断:
**习惯用法**
@@ -116,7 +116,6 @@ if value, ok := readData(); ok {
**注意事项**
如果您像下面一样,没有为多返回值的函数准备足够的变量来存放结果:
```go
func mySqrt(f float64) (v float64, ok bool) {
if f < 0 { return } // error case
@@ -140,7 +139,7 @@ if ok { fmt.Println(t) }
**注意事项 2**
当您将字符串转换为整数时,且确定转换一定能够成功时,可以将 `Atoi` 函数进行一层忽略错误的封装:
当您将字符串转换为整数时,且确定转换一定能够成功时,可以将 `Atoi()` 函数进行一层忽略错误的封装:
```go
func atoi (s string) (n int) {
@@ -149,13 +148,13 @@ func atoi (s string) (n int) {
}
```
实际上,`fmt` 包(第 4.4.3 节)最简单的打印函数也有 2 个返回值:
实际上,`fmt` 包([第 4.4.3 节](04.4.md))最简单的打印函数也有 2 个返回值:
```go
count, err := fmt.Println(x) // number of bytes printed, nil or 0, error
```
当打印到控制台时,可以将该函数返回的错误忽略;但当输出到文件流、网络流等具有不确定因素的输出对象时,应该始终检查是否有错误发生(另见练习 6.1b)。
当打印到控制台时,可以将该函数返回的错误忽略;但当输出到文件流、网络流等具有不确定因素的输出对象时,应该始终检查是否有错误发生(另见[练习 6.1b](06.1.md))。
## 链接