|
|
3 дней назад | |
|---|---|---|
| .. | ||
| README.md | 3 дней назад | |
| main.go | 3 дней назад | |
之前用 bool 表达成功或失败,现在让函数返回失败原因。学会 (结果, error)、if err != nil、提前返回,以及向调用方传递错误。
按 main.go 逐段手敲。本课只使用标准库 errors、fmt、strconv,无需安装依赖。暂不引入自定义错误类型、错误包装、panic / recover 或匿名函数。
func divide(a, b int) (float64, error) {
if b == 0 {
return 0, errors.New("除数不能为 0")
}
return float64(a) / float64(b), nil
}
逐项理解:
(float64, error):两个返回值,第一个是结果,第二个是错误。error:Go 预定义的接口类型,现在先理解成“保存错误信息的类型”,接口细节后续学习。errors.New(...):根据一段文字创建错误值,不会自动打印,也不会自动终止程序。nil:在这里表示没有错误,不是字符串 "nil"。error 不是关键字,也不是异常抛出语法。Go 的这种常规错误处理依靠返回值和显式判断。
result, err := divide(10, 4)
if err != nil {
fmt.Println("计算失败:", err)
} else {
fmt.Println("计算结果:", result)
}
err 是普通变量名,习惯用它保存错误。err != nil 表示发生了错误;err == nil 表示本次调用成功。
不要通过结果是不是 0 判断成功:0 本身可能是合法结果。例如本课 parseScore("0") 返回 (0, nil)。
本课为方便观察,在 main 中既展示成功也展示失败。实际辅助函数中常写成:
result, err := divide(10, 4)
if err != nil {
return err // 这段需要放在返回 error 的函数中。
}
// 下面只处理成功路径,使用 result。
这个 return 只退出当前函数,不自动退出整个程序。返回类型必须匹配,不能在无返回值的 main 中直接写 return err。
本课失败时约定忽略结果;标准库中某些函数会同时返回部分结果和错误,应以具体文档约定为准。
func validateScore(score int) error {
if score < 0 || score > 100 {
return fmt.Errorf("成绩 %d 无效,应在 0~100 之间", score)
}
return nil
}
有些操作没有额外结果,只有“成功或失败”,就只返回 error。
fmt.Errorf 创建带格式的错误信息,支持 %d、%s 等占位符;它与 fmt.Printf 不同,不会直接输出到终端。
score, err := strconv.Atoi(text)
if err != nil {
return 0, err
}
strconv.Atoi 将十进制字符串转换为 int;例如 "85" 成功,"abc" 失败。超出 int 可表示范围也会失败。它不会自动去掉字符串两端的空白。
parseScore 的执行顺序:
函数内部负责计算和返回错误,main 负责决定怎样显示、继续还是停止,避免同一个错误在每一层重复打印。
源码中第二次给 err 赋值使用 err = validateScore(score),因为 err 已存在,不需要再次用 :=。
普通 error 是可供调用方检查的返回值,适合无效输入、找不到文件、网络失败等情况。只要调用方处理得当,程序可以继续执行。
panic 是另一种运行时机制,例如解引用 nil 指针可能引发 panic;它不是 error 返回值,也不会自动被 if err != nil 捕获。本课不使用 panic 处理普通输入错误。
不要为了省事写 score, _ := strconv.Atoi(text) 忽略错误,否则转换失败可能被误当作合法的 0。以后学习错误分类时,也不要仅凭错误文字来判断错误种类。
在仓库根目录执行:
New-Item -ItemType Directory -Force bin/11_errors | Out-Null
go build -o ./bin/11_errors/errors.exe ./lessons/11_errors
.\bin\11_errors\errors.exe
修改源码后需要重新编译。快速编译并运行:
go run ./lessons/11_errors
或从根目录进入本课:
Set-Location lessons/11_errors
go run .
=== 成功返回 nil ===
10 / 4 = 2.50
=== 失败返回原因 ===
计算失败: 除数不能为 0
=== 只有 error 返回值 ===
校验失败: 成绩 120 无效,应在 0~100 之间
=== 字符串转换与错误传递 ===
输入 "85" 成功:成绩=85
输入 "0" 成功:成绩=0
输入 "120" 失败:成绩 120 无效,应在 0~100 之间
输入 "abc" 失败:strconv.Atoi: parsing "abc": invalid syntax
处理结束:错误已由调用方处理,程序继续运行
标准库错误文字可能随版本变化,理解含义即可,不要把完整字符串当作稳定接口。
"100"、"-1"、"101"、"80.5",先预测再运行。" 85 ",确认空白不会被 Atoi 自动忽略。checkAge(age int) error,为自己约定的合法年龄范围返回 nil 或错误。若另行手敲,放到本课的 practice/ 子目录,用 go run ./lessons/11_errors/practice 运行,不要在同一个包中重复定义 main 或同名函数。
下一课学习 switch 与 defer,分别处理多分支选择和函数退出前的操作。